如何修改在 Azure Functions 中本机注入的 IConfiguration

use*_*346 6 c# dependency-injection azure-functions

我们需要配置提供程序添加到本机提供给 Azure Functions 的本机 IConfiguration。目前,我们正在使用以下代码其完全替换为我们的自定义 Iconfiguration:

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        ...

        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddAzureKeyVault(...)
            .AddJsonFile("local.settings.json", true, true)
            .AddEnvironmentVariables()
            .Build();

        builder.Services.AddSingleton<IConfiguration>(configuration);

        builder.Services.AddSingleton<IMyService, MyService>();
    }
}
Run Code Online (Sandbox Code Playgroud)

一些上下文

MyService 在其构造函数中需要来自 KeyVault 提供程序的值以及 Application Insights 等其他实例。如果我们保留默认的 IConfiguration,则它没有 KeyVault 值。如果我们用工厂创建 MyService 实例,我们需要手动提供 App Insights 实例等。目前替换 IConfiguration 编译和函数运行。但它打破了其他默认行为,例如不从 host.json 获取配置(我们正在尝试配置队列触发器)。使用默认的 IConfiguration 可以正确读取 host.json 中的设置。

lop*_*oni 11

有一些关于使用 .NET Core Azure 函数的评论:

  1. 在本地运行时,框架默认为您加载 local.settings.json。
  2. 在运行消耗计划时,您应该避免从 appsettings.json 或其他文件中读取配置值。 函数文档
  3. 一般来说,你应该避免绕过 IConfiguration对象。正如@Dusty 提到的,更喜欢的方法是使用IOptions模式。
  4. 如果您尝试从 Azure Key Vault 读取值,则不需要添加 ,AddAzureKeyVault()因为您可以并且应该使用 Azure Key Vault 引用在 azure 门户中进行配置。密钥保管库文档。通过这样做,azure 函数配置机制不知道/关心它在哪里运行,如果你在本地运行,它会从 local.settings.json 加载,如果它被部署,那么它会从 Azure 应用程序配置中获取值和如果您需要 Azure Key Vault 集成,则可以通过 Azure Key Vault 参考来完成。
  5. 我认为 Azure 功能配置与使用 appsettings.json 的传统 .NET 应用程序不同,这也是关键所在。
  6. 配置 azure 功能应用程序可能会变得很麻烦,因为您需要一一添加设置。我们通过使用Azure 应用配置解决了这个问题您也可以将其连接到 Azure Key Vault。
  7. Application Insights 由 Azure Functions 自动添加。文档

话虽如此,即使不建议执行以下操作,您仍然可以完成您想要的操作。请记住,您还可以通过以下代码在以下代码中添加密钥保管库引用AddAzureKeyVault()

var configurationBuilder = new ConfigurationBuilder();
var descriptor = builder.Services.FirstOrDefault(d => d.ServiceType == typeof(IConfiguration));
if (descriptor?.ImplementationInstance is IConfiguration configRoot)
{
    configurationBuilder.AddConfiguration(configRoot);
}

// Conventions are different between Azure and Local Development and API
// https://github.com/Azure/Azure-Functions/issues/717
// Environment.CurrentDirectory doesn't cut it in the cloud.
// /sf/ask/3893175891/
var localRoot = Environment.GetEnvironmentVariable("AzureWebJobsScriptRoot");
var actualRoot = $"{Environment.GetEnvironmentVariable("HOME")}/site/wwwroot";
var basePath = localRoot ?? actualRoot;
var configuration = configurationBuilder
    .SetBasePath(basePath)
    .AddJsonFile("local.settings.json", optional: true, reloadOnChange: false)
    .AddEnvironmentVariables()
    .Build();

builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IConfiguration), configuration));
Run Code Online (Sandbox Code Playgroud)

如果您需要更多的输入/说明,请告诉我,我会相应地更新我的答案。

  • 反应非常好。解释得很好,提供了所有选项、文档链接,甚至还给出了代码示例。希望收到 OP 的来信并确认这是否有效。 (2认同)

Dus*_*sty 1

选项 1:先于其他配置提供程序引入基本配置

// ...

var baseConfig = builder.Services.BuildServiceProvider().GetService<IConfiguration>();

var configuration = new ConfigurationBuilder()
    .AddConfiguration(baseConfig)
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddAzureKeyVault(...)
    .AddJsonFile("local.settings.json", true, true)
    .AddEnvironmentVariables()
    .Build();

// ...
Run Code Online (Sandbox Code Playgroud)

选项 2:使您的自定义依赖项更细粒度并使用IOptions<T>

不要替换注入的 IConfiguration 实例或使用上面的选项 1,而是让您的下游服务依赖于IOptions<T>. 我认为这是更好的模式,因为您可以根据您的需求将配置分成小部分,并使您的服务采用更有针对性的依赖项。

var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddAzureKeyVault(...)
            .AddJsonFile("local.settings.json", true, true)
            .AddEnvironmentVariables()
            .Build();

// don't re-bind IConfiguration
// builder.Services.AddSingleton<IConfiguration>(configuration);

// instead, bind sections specific to what services may need
builder.Services.Configure<MyDbConfig>(config.GetSection("DbConfig"));
Run Code Online (Sandbox Code Playgroud)

在这种情况下,MyDbConfig只是一个 POCO,其属性用于托管您的配置。您的服务类依赖于IOptions<MyDbConfig>

public class MyService : IMyService
{
    private MyDbConfig _dbConfig;
    public MyService(IOptions<MyDbConfig> myDbConfig)
    {
        _dbConfig = myDbConfig.Value;
    }
}
Run Code Online (Sandbox Code Playgroud)

在基于 JSON 的配置中,您将包含“DbConfig”(或您提供给的任何参数GetSection)作为顶级 JSON 对象,并将您的配置值作为该对象中的属性:

{
    "DbConfig": { "SuperSecretValue": "abc123" }
}
Run Code Online (Sandbox Code Playgroud)

在 KeyVault 中,您可以使用--诸如 之类的秘密名称来指示嵌套模式DbConfig--SuperSecretValue