如何在AWS Lambda C#实现中使用依赖注入

Yad*_*raj 14 c# .net-core aws-lambda

我使用AWS.Net SDK,.net核心版本1.0创建了Lambda函数.我想实现依赖注入.由于lambda函数在AWS环境中独立触发和运行,因此不存在类似的类Startup.如何以及在哪里可以配置我的容器来实现此实现?

Chr*_*gis 23

我知道我参加比赛已经很晚了,但我添加了这一点,因为我相信互联网上有一些不好的/缺乏的例子。@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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这种模式,每个 lambda 调用都会ServiceProvider在完成时获得一个新的并处理它。

  • 这个答案仍然可以改进,因为它在每次调用上构建 DI,这不会让 lambda 重用 aws lambda 的已保存容器上下文。我认为我们应该只在容器为空时重建容器。 (3认同)

小智 18

虽然 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'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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在当前的实现中,您的 Scoped 服务将以与 Singleton 服务相同的方式创建(在每次调用 lambda 函数时),因为您无法控制 ServiceProvider 的范围。为了解决这个问题,“FunctionHandler”应该以:“using varscope = ServiceProvider.CreateScope();”开头。scope.ServiceProvider.GetRequiredService&lt;App&gt;().Run(); //下面的其余代码` (5认同)
  • 使构造函数静态,这有效。现在您有一个在实例之间共享的静态服务提供者,但如果 AWS 在同一个微虚拟机上创建一个新实例,它将再次覆盖服务集合。 (3认同)

Don*_*uts 8

你可以这样做.您的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";
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您想连接日志记录,请查看:https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.Logging.AspNetCore

  • 这是不正确且危险的。您将在每个请求上创建一个新的服务集合。因此,您的“单身人士”将不是在请求之间共享的实际单身人士。它们仅在请求本身的范围内是单例。在重负载下,如果您使用某些类型的资源,则可能会产生很大的后果。 (3认同)
  • 有用的帖子@Donuts - 谢谢.在您的示例中,它可以帮助其他人显示您如何从ConfigureServices执行"App :: Run".例如`serviceCollection.BuildServiceProvider().GetService <App>().Run()` (2认同)