Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Lambda with .Net core - Enabling Injection for IOptions?

I've created an empty AWS Lambda project with .net CORE :

enter image description here

Which basically all it yields is a .net core empty lambda function project :

enter image description here

But as it is now - it doesn't support Injection (DI) , since there is no startup file etc ...So basically it is ia .net core project without all its benefits.

However , Tony wrote about it: Add .NET Core DI and Config Goodness to AWS Lambda Functions" :

Basically what he did is to manually make the project support DI.

So I've created a ConfigurationService class :

ConfigureServices.cs

 public interface IConfigurationService
    {
        IConfiguration GetConfiguration();
    }

    public class ConfigurationService : IConfigurationService
    {
        public IConfiguration GetConfiguration()
        {
            return new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();
        }
    }

And then used it in the code , as in a regular way .

So here is the same function as above returning a key from appSettigs , PLUS (!!!) Dependency Injection:

Function.cs

public class Function
    {
        public Function()
        {
            /*MANUALLY MAKING OUR PROJECT TO SUPPORT DI*/
            var serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);
            var serviceProvider = serviceCollection.BuildServiceProvider();
            ConfigService = serviceProvider.GetService<IConfigurationService>();
        }

        public IConfigurationService ConfigService { get; }

        private void ConfigureServices(IServiceCollection serviceCollection)
        {
            serviceCollection.AddOptions();
            serviceCollection.AddTransient<IConfigurationService, ConfigurationService>();

        }


        public string FunctionHandler(string input, ILambdaContext context)
        {
            var res = ConfigService.GetConfiguration()["Message"];
            return res;
        }
    }

It does work :

enter image description here

(Here is the appsetting.json file BTW) :

appsettings.json

{
    "Message": "Hello"
}

It also work when I Inject IConfigurationService to other class'es constructor.

So where is the problem?

I want to use the type safe access to the appsettings.json using IOptions.
— So I've created a corresponding file:

AppSettings.cs

 public class AppSettings
    {
        public string Message  { get; set; }
    }

And now I need to configure it , but look what happens:

enter image description here

It first invokes the ConfigureServices method in line #16 , then It Configure the AppSettings at line #27 , and then it crashes(!) at line #27 becuase ConfigService is null. Why is it null ? Because it is only assigned in line #18

Question

What should I do in order for the code to support Configure<Appsettings> ?

like image 830
Royi Namir Avatar asked Feb 20 '19 22:02

Royi Namir


1 Answers

You do not really need IOptions in this scenario and can configure the dependencies as needed

For example review the following

public class Function {
    public static Func<IServiceProvider> ConfigureServices = () => {
        var serviceCollection = new ServiceCollection();
        
        serviceCollection.AddOptions(); //OPTIONAL
        
        //...add additional services as needed
        
        //building configuration
        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        
        //Get strongly typed setting from appsettings binding to object graph
        var settings = configuration.Get<AppSettings>();
        // adding to service collection so that it can be resolved/injected as needed.
        serviceCollection.AddSingleton(settings);
        
        return serviceCollection.BuildServiceProvider();
    };

    static IServiceProvider services;
    
    static Function() { //Static ctor invokes once.
        services = ConfigureServices();
    }

    public string FunctionHandler(string input, ILambdaContext context) {
        //...
    
        var settings =  services.GetRequiredService<AppSettings>();
        var message = settings.Message;
        return message;
    }
}

A one time setup of static service provider is configured with the desired settings added to the provider as a strongly typed model which can be resolved and injected as needed.

like image 125
Nkosi Avatar answered Nov 10 '22 16:11

Nkosi