Как использовать инъекцию зависимостей в реализации AWS Lambda С#

Я создал функции Lambda, используя AWS.Net SDK,.net core version 1.0. Я хочу внедрить инъекцию зависимостей. Поскольку функции лямбда запускаются и запускаются независимо в среде AWS, такой класс, как Startup отсутствует. Как и где я могу настроить контейнеры для достижения этой реализации?

Ответ 1

Ты можешь сделать это. Ваш FunctionHandler является вашей точкой входа в ваше приложение... поэтому вам нужно подключить набор служб оттуда.

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";
    }
}

Если вы хотите подключиться к журналу, посмотрите здесь: https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.Logging.AspNetCore

Ответ 2

Хотя FunctionHandler действительно является вашей точкой входа в ваше приложение, я бы фактически связал ваш DI в конструкторе без параметров. Конструктор вызывается только один раз, так что этот чисто "установочный" код нужно вызывать только один раз. Мы просто хотим использовать его в каждом последующем вызове, который направляется в тот же контейнер.

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 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;
    }
}

Ответ 3

Если вы говорите о внедрении зависимостей AWS-сервисов для WebAPI, это возможно, если вы используете шаблон dotnet new lambda.AspNetCoreWebAPI через dotnet new lambda.AspNetCoreWebAPI или Visual Studio чертежи

Этот шаблон имеет класс Startup (конечно, каждый запуск выполняется один раз на каждую среду lambda, как вы упомянули). Вы добавляете дополнительные услуги AWS в ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc();
  services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
  // Add service to obtain in Controllers constructor
  services.AddAWSService<IAmazonDynamoDB>();
}

Затем используйте конструктор для инъекции зависимости вашего класса Controller

IAmazonDynamoDB client;
public ValuesController(IAmazonDynamoDB dbClient)
{
    this.client = dbClient;
}

Эти службы запускаются с учетными данными, полученными из переменных среды, поэтому убедитесь, что вы включили свой профиль AWS в appsettings.json. Если вы не уверены в appsettings.json или как объявлять профили в соответствии с ASPNETCORE_ENVIRONMENT, оставляйте комментарий.

Ответ 4

Я знаю, что опаздываю к игре, но добавляю это, потому что считаю, что в Интернете есть несколько плохих/недостающих примеров. @Erndob прав насчет принятого ответа. Вы просто создадите больше экземпляров.

В зависимости от того, какие регистрации вы делаете в своем DI-контейнере, вам следует помнить:

  1. Какие регистрации вы делаете для реализации IDisposable
  2. Как долго AWS хранит экземпляр вашего объекта. Я не смог найти никакой документации по этому вопросу.

В итоге получилось что-то вроде этого:

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>();
    }
}

С этим шаблоном каждый лямбда-вызов получит новый ServiceProvider и избавится от него после завершения.