How to use Dependency Injection in AWS Lambda C# implementation

I know I am way late to the game but I am adding this because I believe there are some bad/lacking examples out on the internet. @Erndob is right about the accepted answer. You'll just be creating more instances.

Depending on what registrations you are making in your DI container you need to keep in mind:

  1. What registrations are you making that implement IDisposable
  2. How long does AWS keep the instance of your object around. I have not been able to find any documentation on this.

Ended up going with something like this:

public class Function
{
    private ServiceCollection _serviceCollection;

    public Function()
    {
        ConfigureServices();
    }

    public string FunctionHandler(string input, ILambdaContext context)
    {
        using (ServiceProvider serviceProvider = _serviceCollection.BuildServiceProvider())
        {
            // entry to run app.
            return serviceProvider.GetService<App>().Run(input);
        }
    }

    private void ConfigureServices()
    {
        // add dependencies here
        _serviceCollection = new ServiceCollection();
        _serviceCollection.AddTransient<App>();
    }
}

With this pattern each lambda invocation will get a new ServiceProvider and dispose of it when finished.


While the FunctionHandler is indeed your entry point to your application, I would actually wire up your DI in a parameterless constructor. The constructor only ever gets called once, so this purely "setup" code should really only need to be called once. We just want to take advantage of using it in every subsequent invocation that gets routed to the same container.

public class Function
{
    private static ServiceProvider ServiceProvider { get; set; }

    /// <summary>
    /// The parameterless constructor is what Lambda uses to construct your instance the first time.
    /// It will only ever be called once for the lifetime of the container that it's running on.
    /// We want to build our ServiceProvider once, and then use the same provider in all subsequent 
    /// Lambda invocations. This makes things like using local MemoryCache techniques viable (Just 
    /// remember that you can never count on a locally cached item to be there!)
    /// </summary>
    public Function()
    {
        var services = new ServiceCollection();
        ConfigureServices(services);
        ServiceProvider = services.BuildServiceProvider();
    }

    public async Task FunctionHandler(SQSEvent evnt, ILambdaContext context)
    {
        await ServiceProvider.GetService<App>().Run(evnt);
    }

    /// <summary>
    /// Configure whatever dependency injection you like here
    /// </summary>
    /// <param name="services"></param>
    private static void ConfigureServices(IServiceCollection services)
    {
        // add dependencies here ex: Logging, IMemoryCache, Interface mapping to concrete class, etc...

        // add a hook to your class that will actually do the application logic
        services.AddTransient<App>();
    }

    /// <summary>
    /// Since we don't want to dispose of the ServiceProvider in the FunctionHandler, we will
    /// at least try to clean up after ourselves in the destructor for the class.
    /// </summary>
    ~Function()
    {
        ServiceProvider.Dispose();
    }
}

public class App
{
    public async Task Run(SQSEvent evnt)
    {
        // actual business logic goes here
        await Task.CompletedTask;
    }
}

You can do this. Your FunctionHandler is your entry point to your application.. so you have to wire up the service collection from there.

public class Function
{
    public string FunctionHandler(string input, ILambdaContext context)
    {
        var serviceCollection = new ServiceCollection();
        ConfigureServices(serviceCollection);

        // create service provider
        var serviceProvider = serviceCollection.BuildServiceProvider();

        // entry to run app.
        return serviceProvider.GetService<App>().Run(input);
    }

    private static void ConfigureServices(IServiceCollection serviceCollection)
    {
        // add dependencies here

        // here is where you're adding the actual application logic to the collection
        serviceCollection.AddTransient<App>();
    }
}

public class App
{
    // if you put a constructor here with arguments that are wired up in your services collection, they will be injected.

    public string Run(string input)
    {
        return "This is a test";
    }
}

If you want to wire up logging, have a look here: https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.Logging.AspNetCore