At the moment dependency injection is not available out of the box for lambdas. The handler must either be a method of a class with an empty constructor, or of a static class. This leads to patterns which are not in line with modern C# code.
I believe this should be rectified by changing the way the handler class is being instantiated, allowing for dependency injection via constructor arguments.
Describe the Feature
An extension point for the user to register their dependencies (with their corresponding lifecycle) will be exposed, allowing the user to configure the application. When the handler class is instantiated, instead of using Activator.CreateInstance for the generation for the instantiation, a configuration aware factory will be used. Leveraging dotnet's IServiceProvider interface seems like the most idiomatic approach.
Is your Feature Request related to a problem?
With the prevalence of standardized DI in all modern dotnet core programming paradigms, not being able to follow standardized practices when developing lambdas could be a deterrent to developers.
Proposed Solution
My proposed solution is to change the lambda runtime to allow the user to designate a factory for an instance of IServiceProvider, which will then be used to instantiate the handler classes. This can be achieved via convention, an interface or an attribute.
Here's a sample implementation:
First we define an assembly attribute LambdaServiceProviderAttribute that points to a type implementing IServiceProvider. This belongs to the Amazon.Lambda.Core dll:
[AttributeUsage(AttributeTargets.Assembly)]
public class LambdaServiceProviderAttribute : Attribute
{
public LambdaServiceProviderAttribute(Type type)
{
Type = type;
}
public Type Type { get; }
}
Then, in the Amazon.Lambda.RunetimeSupport dll we modify the method UserCodeLoader.Init to get the type from the global attribute and instantiate it, so that we may use it for resolving our handler class:
public void Init(Action<string> customerLoggingAction)
{
...
- var customerObject = GetCustomerObject(customerType);
+ var attr = customerAssembly.GetCustomAttribute<LambdaServiceProviderAttribute>();
+ var services = attr != null ? (IServiceProvider) GetCustomerObject(attr.Type, null) : null;
+ var customerObject = GetCustomerObject(customerType, services);
...
and UserCodeLoader.GetCustomerObject to potentially accept an IServiceProvider and use it if it is present:
- private object GetCustomerObject(Type customerType)
+ private object GetCustomerObject(Type customerType, IServiceProvider services)
{
...
- return Activator.CreateInstance(customerType);
+ return services?.GetService(customerType) ?? Activator.CreateInstance(customerType);
}
A consumer can then trivially add to their lambda code a snippet such as:
[assembly: LambdaServiceProvider(typeof(SampleServiceProvider))]
public class SampleServiceProvider : IServiceProvider
{
private readonly ServiceProvider services;
public SampleServiceProvider()
{
services = new ServiceCollection()
.AddSingleton<Handler>()
.AddSingleton(new Foo("hello world"))
.BuildServiceProvider();
}
public object GetService(Type serviceType) => services.GetService(serviceType);
}
As noted above, any variety of ways can be used to allow the user to use this extension point, assembly attribute is just a suggestion which goes in line with precedents such as LambdaSerializer.
Describe alternatives you've considered
There are a couple of possible approaches. One is to instantiate a DI resolution root (ie ServiceCollection) in the handler's argument-less constructor, and then use it to resolve your dependencies. This is the suggestion currently provided by google when searching for this. As an extension to it, there are a couple of libraries wrapping this in a slightly nicer way such as Kralizek Lambda Template and Tiger-Lambda, but they're effectively all functionally equivalent, and all causing unnecessary boilerplate code.
The second option is to deploy a self-contained lambda. By using lower level methods from Amazon.Lambda.RuntimeSupport, the user is able to bootstrap the lambda with an already resolved delegate through an external pipeline. This feels a little like a sledgehammer approach, customizing the entire runetime for a single minor concern.
Additional Context
Coming to lambda development from Azure Functions, the first thing I tried doing was to enable dependency injection. When I was unable to find satisfactory answers online, I was frustrated as it seemed completely unreasonable for me that this feature was missing. I thought it was more likely I couldn't find a solution, than that one didn't exist.
As someone with a considerable experience in the dotnet space, I would personally classify this feature missing as "unreasonable".
I believe that most users would need this feature, and are currently implementing some sort of a workaround to achieve it, potentially causing other issues in the process. It would be highly beneficial for the ecosystem for an idiomatic approach to not only exist, but for it to be actively supported and advocated for by the vendor.
Environment
This feature is applicable to all lambdas running dotnet code.
- [✅] 👋 I may be able to implement this feature request
- [❎ ] ⚠️ This feature might incur a breaking change
This is a 🚀 Feature Request
At the moment dependency injection is not available out of the box for lambdas. The handler must either be a method of a class with an empty constructor, or of a static class. This leads to patterns which are not in line with modern C# code.
I believe this should be rectified by changing the way the handler class is being instantiated, allowing for dependency injection via constructor arguments.
Describe the Feature
An extension point for the user to register their dependencies (with their corresponding lifecycle) will be exposed, allowing the user to configure the application. When the handler class is instantiated, instead of using
Activator.CreateInstancefor the generation for the instantiation, a configuration aware factory will be used. Leveraging dotnet'sIServiceProviderinterface seems like the most idiomatic approach.Is your Feature Request related to a problem?
With the prevalence of standardized DI in all modern dotnet core programming paradigms, not being able to follow standardized practices when developing lambdas could be a deterrent to developers.
Proposed Solution
My proposed solution is to change the lambda runtime to allow the user to designate a factory for an instance of
IServiceProvider, which will then be used to instantiate the handler classes. This can be achieved via convention, an interface or an attribute.Here's a sample implementation:
First we define an assembly attribute
LambdaServiceProviderAttributethat points to a type implementingIServiceProvider. This belongs to theAmazon.Lambda.Coredll:Then, in the
Amazon.Lambda.RunetimeSupportdll we modify the methodUserCodeLoader.Initto get the type from the global attribute and instantiate it, so that we may use it for resolving our handler class:public void Init(Action<string> customerLoggingAction) { ... - var customerObject = GetCustomerObject(customerType); + var attr = customerAssembly.GetCustomAttribute<LambdaServiceProviderAttribute>(); + var services = attr != null ? (IServiceProvider) GetCustomerObject(attr.Type, null) : null; + var customerObject = GetCustomerObject(customerType, services); ...and
UserCodeLoader.GetCustomerObjectto potentially accept anIServiceProviderand use it if it is present:A consumer can then trivially add to their lambda code a snippet such as:
As noted above, any variety of ways can be used to allow the user to use this extension point, assembly attribute is just a suggestion which goes in line with precedents such as
LambdaSerializer.Describe alternatives you've considered
There are a couple of possible approaches. One is to instantiate a DI resolution root (ie ServiceCollection) in the handler's argument-less constructor, and then use it to resolve your dependencies. This is the suggestion currently provided by google when searching for this. As an extension to it, there are a couple of libraries wrapping this in a slightly nicer way such as Kralizek Lambda Template and Tiger-Lambda, but they're effectively all functionally equivalent, and all causing unnecessary boilerplate code.
The second option is to deploy a self-contained lambda. By using lower level methods from
Amazon.Lambda.RuntimeSupport, the user is able to bootstrap the lambda with an already resolved delegate through an external pipeline. This feels a little like a sledgehammer approach, customizing the entire runetime for a single minor concern.Additional Context
Coming to lambda development from Azure Functions, the first thing I tried doing was to enable dependency injection. When I was unable to find satisfactory answers online, I was frustrated as it seemed completely unreasonable for me that this feature was missing. I thought it was more likely I couldn't find a solution, than that one didn't exist.
As someone with a considerable experience in the dotnet space, I would personally classify this feature missing as "unreasonable".
I believe that most users would need this feature, and are currently implementing some sort of a workaround to achieve it, potentially causing other issues in the process. It would be highly beneficial for the ecosystem for an idiomatic approach to not only exist, but for it to be actively supported and advocated for by the vendor.
Environment
This feature is applicable to all lambdas running dotnet code.
This is a 🚀 Feature Request