配置服务时如何在具有依赖关系注入的Azure Function V3中注入或使用IConfiguration [英] How to inject or use IConfiguration in Azure Function V3 with Dependency Injection when configuring a service

查看:86
本文介绍了配置服务时如何在具有依赖关系注入的Azure Function V3中注入或使用IConfiguration的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常,在.NET Core项目中,我将创建一个"boostrap"类来配置我的服务以及DI注册命令.这通常是 IServiceCollection 的扩展方法,在这里我可以调用类似 .AddCosmosDbService 的方法,并且在包含该方法的静态类中,所有必需的东西都是自包含的".但是关键是该方法从 Startup 类获取了 IConfiguration .

Normally in a .NET Core project I would create a 'boostrap' class to configure my service along with the DI registration commands. This is usually an extension method of IServiceCollection where I can call a method like .AddCosmosDbService and everything necessary is 'self-contained' in the static class containing that method. The key though is that the method gets an IConfiguration from the Startup class.

我过去曾在Azure Functions中使用过DI,但尚未遇到此特定要求.

I've worked with DI in Azure Functions in past but haven't come across this specific requirement yet.

我正在使用 IConfiguration 绑定到具有与我的 local.settings.json 以及开发/生产应用程序设置中的设置匹配的属性的具体类在Azure中部署该功能时.

I'm using the IConfiguration to bind to a concrete class with properties matching settings from both my local.settings.json as well as the dev/production application settings when the Function is deployed in Azure.

/// <summary>
/// Holds configuration settings from local.settings.json or application configuration
/// </summary>    
public class CosmosDbClientSettings
{
    public string CosmosDbDatabaseName { get; set; }
    public string CosmosDbCollectionName { get; set; }
    public string CosmosDbAccount { get; set; }
    public string CosmosDbKey { get; set; }
}

BootstrapCosmosDbClient.cs

public static class BootstrapCosmosDbClient
{
    /// <summary>
    /// Adds a singleton reference for the CosmosDbService with settings obtained by injecting IConfiguration
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <returns></returns>
    public static async Task<CosmosDbService> AddCosmosDbServiceAsync(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        CosmosDbClientSettings cosmosDbClientSettings = new CosmosDbClientSettings();
        configuration.Bind(nameof(CosmosDbClientSettings), cosmosDbClientSettings);

        CosmosClientBuilder clientBuilder = new CosmosClientBuilder(cosmosDbClientSettings.CosmosDbAccount, cosmosDbClientSettings.CosmosDbKey);
        CosmosClient client = clientBuilder.WithConnectionModeDirect().Build();
        CosmosDbService cosmosDbService = new CosmosDbService(client, cosmosDbClientSettings.CosmosDbDatabaseName, cosmosDbClientSettings.CosmosDbCollectionName);
        DatabaseResponse database = await client.CreateDatabaseIfNotExistsAsync(cosmosDbClientSettings.CosmosDbDatabaseName);
        await database.Database.CreateContainerIfNotExistsAsync(cosmosDbClientSettings.CosmosDbCollectionName, "/id");

        services.AddSingleton<ICosmosDbService>(cosmosDbService);

        return cosmosDbService;
    }
}

Startup.cs

public class Startup : FunctionsStartup
{

    public override async void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddHttpClient();
        await builder.Services.AddCosmosDbServiceAsync(**need IConfiguration reference**); <--where do I get IConfiguration?
    }
}

显然在 Startup.cs 中为 IConfiguration 添加一个私有字段将无法正常工作,因为它需要填充一些东西,而且我也读过将DI用于 IConfiguration 并不是一个好主意.

Obviously adding a private field for IConfiguration in Startup.cs won't work as it needs to be populated with something and I've also read that using DI for IConfiguration isn't a good idea.

我还尝试过使用

I've also tried using the options pattern as described here and implemented as such:

builder.Services.AddOptions<CosmosDbClientSettings>()
    .Configure<IConfiguration>((settings, configuration) => configuration.Bind(settings));

虽然这可以为非静态类注入 IOptions< CosmosDbClientSettings> ,但我使用的是静态类来保存我的配置工作.

While this would work to inject an IOptions<CosmosDbClientSettings> to a non-static class, I'm using a static class to hold my configuration work.

关于如何进行这项工作或可能的解决方法的任何建议?我希望将所有配置都放在一个位置(引导文件).

Any suggestions on how I can make this work or a possible workaround? I'd prefer to keep all the configuration in one place (bootstrap file).

推荐答案

The linked example is poorly designed (In My Opinion). It encourages tight coupling and the mixing of async-await and blocking calls.

IConfiguration 作为启动的一部分默认添加到服务集合中,因此,我建议更改设计以利用延迟的依赖关系解析,以便 IConfiguration可以使用工厂委托通过内置的 IServiceProvider 进行解析.

IConfiguration is added to the service collection by default as part of the start up, so I would suggest changing up your design to take advantage of the deferred resolution of dependencies so that the IConfiguration can be resolved via the built IServiceProvider using a factory delegate.

public static class BootstrapCosmosDbClient {

    private static event EventHandler initializeDatabase = delegate { };

    public static IServiceCollection AddCosmosDbService(this IServiceCollection services) {

        Func<IServiceProvider, ICosmosDbService> factory = (sp) => {
            //resolve configuration
            IConfiguration configuration = sp.GetService<IConfiguration>();
            //and get the configured settings (Microsoft.Extensions.Configuration.Binder.dll)
            CosmosDbClientSettings cosmosDbClientSettings = configuration.Get<CosmosDbClientSettings>();
            string databaseName = cosmosDbClientSettings.CosmosDbDatabaseName;
            string containerName = cosmosDbClientSettings.CosmosDbCollectionName;
            string account = cosmosDbClientSettings.CosmosDbAccount;
            string key = cosmosDbClientSettings.CosmosDbKey;

            CosmosClientBuilder clientBuilder = new CosmosClientBuilder(account, key);
            CosmosClient client = clientBuilder.WithConnectionModeDirect().Build();
            CosmosDbService cosmosDbService = new CosmosDbService(client, databaseName, containerName);

            //async event handler
            EventHandler handler = null;
            handler = async (sender, args) => {
                initializeDatabase -= handler; //unsubscribe
                DatabaseResponse database = await client.CreateDatabaseIfNotExistsAsync(databaseName);
                await database.Database.CreateContainerIfNotExistsAsync(containerName, "/id");
            };
            initializeDatabase += handler; //subscribe
            initializeDatabase(null, EventArgs.Empty); //raise the event to initialize db

            return cosmosDbService;
        };
        services.AddSingleton<ICosmosDbService>(factory);
        return service;
    }
}

请注意为避免在非异步事件处理程序中使用 async void 而采取的方法.

Note the approach taken to get around the having to use async void in a non-async event handler.

参考因此,现在可以正确调用 Configure .

So now the Configure can be properly invoked.

public class Startup : FunctionsStartup {

    public override void Configure(IFunctionsHostBuilder builder) =>
        builder.Services
            .AddHttpClient()
            .AddCosmosDbService();
}

这篇关于配置服务时如何在具有依赖关系注入的Azure Function V3中注入或使用IConfiguration的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆