如何在AWS Lambda C#实现中使用依赖注入
我使用AWS.Net SDK、.Net core 1.0版创建了Lambda函数。我想实现依赖注入。由于lambda函数在AWS环境中独立触发和运行,因此不存在类似于如何在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
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问题,但我们也应该考虑单问题。所以我将这个作为扩展的答案。投票表决。你将如何测试?这