簡體   English   中英

如何在AWS Lambda C#實現中使用依賴注入

[英]How to use Dependency Injection in AWS Lambda C# implementation

我使用 AWS.Net SDK、.net 核心版本 1.0 創建了 Lambda 函數。 我想實現依賴注入。 由於 lambda 函數在 AWS 環境中觸發並獨立運行,因此不存在像Startup這樣的 class。 如何以及在何處配置我的容器以實現此實現?

我知道我來晚了,但我添加這個是因為我相信 inte.net 上有一些不好的/缺少的例子。 @Erndob 關於已接受的答案是正確的。 您只會創建更多實例。

根據您在 DI 容器中進行的注冊,您需要牢記:

  1. 您要進行哪些注冊以實現 IDisposable
  2. AWS 將您的 object 實例保留多長時間。 我找不到任何關於此的文檔。

結束了這樣的事情:

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

使用此模式,每個 lambda 調用都將獲得一個新的ServiceProvider並在完成后將其處理掉。

你可以這樣做。 您的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

使用常規的 Lambda 項目模板,Scoped lifetime app 似乎不存在,您必須按照上面的答案創建解決方法。

借助新的“.NET Annotations Lambda Framework”,AWS 正試圖解決該問題。

這里的解釋: https://aws.amazon.com/blogs/developer/introducing.net-annotations-lambda-framework-preview/

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

這是另一種處理方法。 這是此處https://aws.amazon.com/blogs/compute/migrating-a-monolithic.net-rest-api-to-aws-lambda/中一些代碼的簡化版本。 一個顯着的區別是BuildServiceProvider()僅在創建 Lambda 時調用一次。

public class Function
{
    private static IServiceProvider services;
    private readonly IBookingRepository bookingRepository;
    private readonly ILogger<Function> logger;

    public Function()
    {
        ConfigureServices();
        
        this.bookingRepository = services.GetRequiredService<IBookingRepository>();
        this.logger = services.GetRequiredService<ILogger<Function>>();
    }

    public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
    {
        if (!apigProxyEvent.PathParameters.ContainsKey("customerId"))
        {
            return new APIGatewayProxyResponse
            {
                StatusCode = 400,
                Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
            };
        }

        var customerId = apigProxyEvent.PathParameters["customerId"];

        this.logger.LogInformation($"Received request to list bookings for: {customerId}");

        var customerBookings = await this.bookingRepository.ListForCustomer(customerId);

        return new APIGatewayProxyResponse
        {
            Body = JsonSerializer.Serialize(customerBookings),
            StatusCode = 200,
            Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
        };
    }

    private void ConfigureServices()
    {
        // Add dependencies here.
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDbContext<BookingContext>(options =>
            options.UseMySQL("ConnectionString..."));
        serviceCollection.AddTransient<IBookingRepository, BookingRepository>();
        serviceCollection.AddLogging(logging =>
        {
            logging.AddLambdaLogger();
            logging.SetMinimumLevel(LogLevel.Debug);
        });
        services = serviceCollection.BuildServiceProvider();
    }
}

如果您正在討論針對WebAPI的AWS服務的依賴注入,則可以通過dotnet new lambda.AspNetCoreWebAPI使用AspNetCoreWebAPI模板獲得dotnet new lambda.AspNetCoreWebAPI或Visual Studio藍圖

這個模板有Startup類(當然每個lambda環境都會執行一次啟動,就像你提到的那樣)。 您在ConfigureServices添加了添加AWS服務

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聲明配置文件發表評論。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM