当前位置: 首页 > 知识库问答 >
问题:

配置服务时,如何在Azure Function V3中注入或使用IConfiguration和依赖项注入

云隐水
2023-03-14

通常在a.NET Core项目我将创建一个引导类来配置我的服务以及DI注册命令。这通常是IServiceCollection的扩展方法,我可以在其中调用像这样的方法。addCosmosDbService和包含该方法的静态类中所需的一切都是自包含的。关键是该方法从Startup类中获取一个IConfiguration

我以前在Azure函数中使用过DI,但还没有遇到这个具体要求。

我正在使用IConfiguration绑定到一个具体的类,该类的属性与我的本地设置匹配。设置。json以及在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; }
}
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;
    }
}
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?
    }
}

显然,在启动中为IConfiguration添加了一个私有字段。cs不起作用,因为它需要填充一些东西,我还读到,在IConfiguration中使用DI不是一个好主意。

我还尝试使用这里描述的选项模式,并实现如下:

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

而这将有助于注入IOptions

关于如何完成这项工作或可能的变通方法的任何建议?我宁愿把所有的配置放在一个地方(引导文件)。


共有3个答案

张嘉
2023-03-14

这里有一个我能够想到的例子;它建立了与Azure应用配置的连接,用于集中配置和功能管理。应该能够使用所有DI功能,例如IConfigurationIOptions

NuGet依赖项

  • 安装-打包Microsoft.天青。函数。扩展
  • 安装-打包Microsoft.扩展。配置。AzureAppConfiguration
  • 安装-打包Microsoft.扩展。配置。用户秘密

Startup.cs

[assembly: FunctionsStartup(typeof(SomeApp.Startup))]

namespace SomeApp
{
    public class Startup : FunctionsStartup
    {
        public IConfigurationRefresher ConfigurationRefresher { get; private set; }

        public override void Configure(IFunctionsHostBuilder hostBuilder) {
            if (ConfigurationRefresher is not null) {
                hostBuilder.Services.AddSingleton(ConfigurationRefresher);
            }
        }
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder configurationBuilder) {
            var hostBuilderContext = configurationBuilder.GetContext();
            var isDevelopment = ("Development" == hostBuilderContext.EnvironmentName);

            if (isDevelopment) {
                configurationBuilder.ConfigurationBuilder
                    .AddJsonFile(Path.Combine(hostBuilderContext.ApplicationRootPath, $"appsettings.{hostBuilderContext.EnvironmentName}.json"), optional: true, reloadOnChange: false)
                    .AddUserSecrets<Startup>(optional: true, reloadOnChange: false);
            }

            var configuration = configurationBuilder.ConfigurationBuilder.Build();
            var applicationConfigurationEndpoint = configuration["APPLICATIONCONFIGURATION_ENDPOINT"];

            if (!string.IsNullOrEmpty(applicationConfigurationEndpoint)) {
                configurationBuilder.ConfigurationBuilder.AddAzureAppConfiguration(appConfigOptions => {
                    var azureCredential = new DefaultAzureCredential(includeInteractiveCredentials: false);

                    appConfigOptions
                        .Connect(new Uri(applicationConfigurationEndpoint), azureCredential)
                        .ConfigureKeyVault(keyVaultOptions => {
                            keyVaultOptions.SetCredential(azureCredential);
                        })
                        .ConfigureRefresh(refreshOptions => {
                            refreshOptions.Register(key: "Application:ConfigurationVersion", label: LabelFilter.Null, refreshAll: true);
                            refreshOptions.SetCacheExpiration(TimeSpan.FromMinutes(3));
                        });

                    ConfigurationRefresher = appConfigOptions.GetRefresher();
                });
            }
        }
    }
}

卢伟志
2023-03-14

在我看来,这个关联的例子设计得很糟糕。它鼓励紧密耦合以及异步等待和阻塞调用的混合。

默认情况下,IConfiguration作为启动的一部分添加到服务集合中,因此我建议更改您的设计,以利用延迟的依赖项解析,这样就可以通过内置的IServiceProvider使用工厂委托解析IConfiguration

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而采取的方法。

参考Async/Await——异步编程中的最佳实践。

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

public class Startup : FunctionsStartup {

    public override void Configure(IFunctionsHostBuilder builder) =>
        builder.Services
            .AddHttpClient()
            .AddCosmosDbService();
}
宰父君昊
2023-03-14

从微软1.1.0版开始。蔚蓝色的功能。您可以执行以下操作:

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        var configuration = builder.GetContext().Configuration;
        builder.Services.AddCosmosDbService(configuration);
    }
}

不幸的是,它仍然不支持异步配置,所以您仍然需要阻止等待任务完成,或者使用@Nkosi的答案描述的技巧。

 类似资料:
  • 我已经使用GoogleGuice几个月了。我对它很满意,但似乎我用错了。我创造了很多辅助注射,有时还有两个注射器。 因此,我想了解这里的一般原则。 是否应该为连接主类所有内容的所有应用程序配备一个喷油器? 最佳实践是关于从应用程序一开始就尝试在构造函数中注入大量内容吗?(我看到了测试的优势) 我的主要问题是,有时我觉得某些对象组的创建属于某个组件。有必要封装它吗?你会如何处理? 当您拥有一个重要的

  • 我们在产品中使用Spring引导微服务,我们有多达10个应用程序。为了记录,我们使用Log4j MDC来生成事务标识,并使用拦截器和过滤器将其传递给服务[超文本传输协议标头]。问题是我们必须在我们所有的应用程序(比如10个)中添加拦截器和过滤器来跟踪这个事务。有没有办法在我们的微服务应用程序中创建jar并注入。 我们能否在所有应用程序中使用最少的代码更改来实现这一点?

  • 本文向大家介绍webapi中如何使用依赖注入,包括了webapi中如何使用依赖注入的使用技巧和注意事项,需要的朋友参考一下 本篇将要和大家分享的是webapi中如何使用依赖注入,依赖注入这个东西在接口中常用,实际工作中也用的比较频繁,因此这里分享两种在api中依赖注入的方式Ninject和Unity;由于快过年这段时间打算了解下vue.js,所以后面对webapi的分享文章可能会慢点更新,希望支持

  • 问题内容: 如何在不使用调用的情况下使用Spring将依赖项注入? 问题答案: 由于Servlet 3.0 ServletContext具有“ addListener”方法,因此可以通过如下代码添加而不是在web.xml文件中添加侦听器: 这意味着你可以正常地注入“ MyHttpSessionListener”中,并且,只要你的应用程序上下文中存在bean,就会使侦听器注册到容器中

  • 1. 前言 上一节,我们通过 xml 文件的配置方式,实现了对多种依赖类型的注入,当然体会到了 xml 文件配置方式的弊端:有一点麻烦。 依赖注入是有两种方式,一种是 xml ,另外一种就是注解的配置方式。 本节,我们演示下通过注解配置这种方式来实现注入依赖。 来吧 ,直入主题,莫浪费大好光阴… 2. 工程实例 2.1 注解的介绍 在正式使用注解之前,我们首先介绍下注解语法以及它的作用。 @Aut

  • 我想用di。在《颤栗》中,我加上这个https://pub.dartlang.org/packages/di打包我的项目,我开始读这篇文章https://webdev.dartlang.org/angular/guide/dependency-injection文章,但我不完全理解。 所以没关系:在服务类(例如:MyServices)上使用@Injectable()注释,但是如何注入其他类呢?例如