Simple Dependency Injection for .NET Lambda Functions

Want to learn more about AWS Lambda and .NET? Check out my A Cloud Guru course on ASP.NET Web API and Lambda.

Download full source code.

Update - I’ve written a few posts on using the Amazon.Lambda.Annotations library to perform dependency injection with .NET serverless AWS Lambda functions. See part 1 and part 2.

If dependency injection is something you use in your .NET projects, you may want to use it with your AWS Lambda functions as well. This post will show you how to set it up in the two most simple Lambda function templates - lambda.EmptyFunction and lambda.EmptyTopLevelFunction.

Dependency injection is set up outside of the function handler. The code will be invoked when the Lambda function is initialized, not each time the handler is called. When the function is initialized, a Lambda execution environment is created, this execution environment will be re-used if the function is invoked frequently. If the function goes idle for a period, the execution environment will be torn down and a new one initialized when the function is invoked again.

The Lambda service lets you run your .NET code in function as a service manner.

Common code

Add the NuGet package Microsoft.Extensions.DependencyInjection to your project.

Create a file called SomeService.cs, it will contain the interface and the class. Add the following code to the file -

 1public class SomeService : ISomeService
 2{
 3    public string ToUpperCase(string text)
 4    {
 5        Console.WriteLine("Inside the DIed service");
 6        return text.ToUpper();
 7    }
 8}
 9
10public interface ISomeService
11{
12    string ToUpperCase(string text);
13}

As you can see, this is very simple, all it does is convert a string to uppercase.

Scoped and singleton services

In the examples below, I use a scoped service in the first and a singleton in the second.

In traditional .NET API applications where multiple requests are handled simultaneously by the same process, scoped services create a new instance of the service for each request. This prevents an instance of a service from being shared between requests.

Lambda functions run inside execution environments, each execution environment handles a single request at a time. Therefore two requests can’t use a single instance of a service at the same time (thanks to my colleague James Eastham for pointing this out to me).

As long are you are not storing any state in the service, a singleton might be a better choice as there will be less overhead. But if you are storing state (or not clearing state) in the service, then with a singleton, the state will be available to the next request.

Adding Scoped DI to lambda.EmptyFunction templates

Open the Function.cs file and replace the code with the following -

 1using Amazon.Lambda.Core;
 2using Microsoft.Extensions.DependencyInjection;
 3
 4// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
 5[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
 6
 7namespace LambdaDIEmptyFunction;
 8
 9public class Function
10{
11    readonly ServiceProvider _serviceProvider;
12    public Function()
13    {
14        Console.WriteLine("Setting up the DI container");
15        var serviceCollection = new ServiceCollection();
16        Console.WriteLine("Adding a scoped service");
17        serviceCollection.AddScoped<ISomeService, SomeService>();
18        _serviceProvider = serviceCollection.BuildServiceProvider();
19    }
20
21    public string FunctionHandler(string input, ILambdaContext context)
22    {
23        string upperCaseText;
24        using (var scope = _serviceProvider.CreateScope())
25        {
26            var someService = scope.ServiceProvider.GetRequiredService<ISomeService>();
27            upperCaseText = someService.ToUpperCase(input);
28        }
29
30        return upperCaseText;
31    }
32}

Lines 11-19, set up the DI container and register the service. This code is run the first time the Lambda function is invoked, as long as the execution environment is not removed, this code won’t be run again.

Lines 24-28, the service is resolved from the DI container and the service is invoked.

Adding Singleton DI to lambda.EmptyTopLevelFunction templates

Open the Function.cs file and replace the code with the following -

 1using Amazon.Lambda.Core;
 2using Amazon.Lambda.RuntimeSupport;
 3using Amazon.Lambda.Serialization.SystemTextJson;
 4using Microsoft.Extensions.DependencyInjection;
 5
 6Console.WriteLine("Setting up the DI container");
 7var serviceCollection = new ServiceCollection();
 8Console.WriteLine("Adding a singleton service");
 9serviceCollection.AddSingleton<ISomeService, SomeService>();
10ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
11
12var handler = (string input, ILambdaContext context) =>
13{   
14    var someService = serviceProvider.GetService<ISomeService>();
15    string upperCaseText = someService.ToUpperCase(input);
16
17    return upperCaseText;
18};
19
20await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer())
21        .Build()
22        .RunAsync();

Lines 7-10, set up the DI container and register the service. This code is run the first time the Lambda function is invoked, as long as the execution environment is not removed, this code won’t be run again.

Lines 14-15, the service is resolved from the DI container, and the service is invoked.

Download full source code.

comments powered by Disqus

Related