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

如何在AWS Lambda C#实现中使用依赖注入,c#,.net-core,aws-lambda,C#,.net Core,Aws Lambda,我使用AWS.Net SDK、.Net core 1.0版创建了Lambda函数。我想实现依赖注入。由于lambda函数在AWS环境中独立触发和运行,因此不存在类似于Startup的类。如何以及在何处配置容器以实现此实现?您可以这样做。FunctionHandler是应用程序的入口点。。因此,您必须从那里连接服务集合 public class Function { public string FunctionHandler(string input, ILambdaContext con

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

您可以这样做。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";
    }
}
公共类函数
{
公共字符串FunctionHandler(字符串输入,ILambdaContext上下文)
{
var servicecolection=新servicecolection();
配置服务(serviceCollection);
//创建服务提供商
var serviceProvider=servicecolection.BuildServiceProvider();
//运行应用程序的入口。

return serviceProvider.GetService

虽然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;
    }
}
公共类函数
{
私有静态服务提供程序服务提供程序{get;set;}
/// 
///Lambda第一次使用无参数构造函数构造实例。
///在运行它的容器的生命周期内,它只会被调用一次。
///我们希望构建一次ServiceProvider,然后在所有后续过程中使用同一个提供程序
///Lambda调用。这使得使用本地MemoryCache技术等技术变得可行(仅此而已)
///请记住,您永远不能指望本地缓存的项目出现在那里!)
/// 
公共职能()
{
var services=newservicecolection();
配置服务(服务);
ServiceProvider=services.BuildServiceProvider();
}
公共异步任务FunctionHandler(SQSEvent evnt、ILambdaContext上下文)
{
等待ServiceProvider.GetService().Run(evnt);
}
/// 
///在这里配置您喜欢的任何依赖项注入
/// 
/// 
专用静态void配置服务(IServiceCollection服务)
{
//在此处添加依赖项,例如:日志记录、IMemoryCache、到具体类的接口映射等。。。
//向类中添加一个钩子,该钩子将实际执行应用程序逻辑
services.AddTransient();
}
/// 
///因为我们不想在FunctionHandler中处理ServiceProvider,所以我们将
///至少试着在类的析构函数中清理一下。
/// 
~Function()
{
Dispose();
}
}
公共类应用程序
{
公共异步任务运行(SQSEvent evnt)
{
//实际的业务逻辑在这里
等待任务。完成任务;
}
}

我知道我玩游戏迟到了,但我之所以添加这一点,是因为我相信互联网上有一些不好的/缺乏的例子。@Erndob关于公认的答案是正确的。你只需要创建更多的例子

根据您在DI容器中进行的注册,您需要记住:

  • 您正在进行哪些注册以使该工具可IDisposable
  • 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>();
        }
    }
    
    公共类函数
    {
    私人ServiceCollection _ServiceCollection;
    公共职能()
    {
    配置服务();
    }
    公共字符串FunctionHandler(字符串输入,ILambdaContext上下文)
    {
    使用(ServiceProvider ServiceProvider=\u serviceCollection.BuildServiceProvider())
    {
    //运行应用程序的入口。
    返回serviceProvider.GetService().Run(输入);
    }
    }
    私有void配置服务()
    {
    //在此处添加依赖项
    _serviceCollection=新serviceCollection();
    _serviceCollection.AddTransient();
    }
    }
    

    使用此模式,每次lambda调用都将获得一个新的
    服务提供程序
    ,并在完成后将其处理。

    我真的认为这不可能。但我可能错了。有帮助的帖子@Donuts-谢谢。在您的示例中,它可能会帮助其他人展示您如何执行“App::Run”从ConfigureServices.g.
    serviceCollection.BuildServiceProvider().GetService().Run()
    这是不正确和危险的。您将在每个请求上创建一个新的服务集合。因此,您的“单例”不会是请求之间共享的实际单例。它们只在请求本身的范围内才是单例。在重载情况下,如果您使用某种类型的资源,可能会产生非常大的后果。@Erndob感谢您指出这一点。Chris Dargis已经对其进行了重构,我认为我们应该在实现时遵循它。让e constructor static,这是可行的。现在您有一个在实例之间共享的静态服务提供程序,但是如果AWS在同一个微vm上创建一个新实例,它将再次覆盖服务集合。在当前实现中,您的作用域服务将以与单例服务相同的方式创建(在每次调用lambda函数时),因为您不控制ServiceProvider的作用域。要解决此问题,
    FunctionHandler
    应以以下开头:`using var scope=ServiceProvider.CreateScope();scope.ServiceProvider.GetRequiredService().Run()你的函数的构造函数在冷启动时只调用一次,对吗?非常感谢克里斯,它永远不会太迟。接受的答案解决了DI问题,但我们也应该考虑单问题。所以我将这个作为扩展的答案。投票表决。你将如何测试?这