新的 Azure Function 3.0 SDK 提供了一种实现启动类的方法。它允许访问通过依赖注入可用的服务集合,我可以在其中添加自己的组件和第三方服务。
但是我不知道如何使用配置文件。
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
...
我的第三方服务将大型结构作为参数,这些配置文件是用二进制文件复制的。我可以将它们复制到appsettings.json文件的子部分中:
{
"MachineLearningConfig" : {
( about 50+ parameters and subsections )
}
}
配置值根据部署环境进行更新。为此,我使用 Azure DevOps 的文件转换任务:生产值不同于暂存和开发值。
根据文档 https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection 加载这些选项的方法是:
builder.Services.AddOptions<MachineLearningConfig>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection("MachineLearningConfig").Bind(settings);
});
但这需要在主机环境中将所有设置添加为键/值字符串,而这正是我不想做的。它们太多了,这不像在 json 配置文件中那样容易维护。
我复制了那个appsettings.json和host.json。
但是 Azure 函数 SDK 在启动时读取的 appsettings.json 文件不是我的应用程序的 appsettings.json,而是 Azure 函数工具的appsettings.json。因此configuration.GetSection("MachineLearningConfig")
返回空值,因为 Azure 函数工具 bin 文件夹中没有appsettings.json文件。
所以,我的问题:如何从我的appsetting.json
文件中读取我的MachineLearningConfig
部分作为IOption<MachineLearningConfig>
注入到我的应用程序中?
在 Azure Functions v3 中,可以将 ASP.NET-Core 中的appsettings.json
配置模式与下面的ConfigureAppConfiguration
调用一起使用(参考(。
此外,使用下面的Configure
方法中的代码更改添加选项的方式。您不应该将IConfiguration
传递给IServiceProvider.Configure<>()
。这将允许您使用注入的IOptions<MachineLearningConfig>
对象。
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
[assembly: FunctionsStartup(typeof(Startup))]
namespace MyAzureFunction
{
public class Startup : FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
var context = builder.GetContext();
builder.ConfigurationBuilder
.AddAppsettingsFile(context)
.AddAppsettingsFile(context, useEnvironment: true)
.AddEnvironmentVariables();
}
public override void Configure(IFunctionsHostBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
var configuration = builder.GetContext().Configuration;
builder.Services.Configure<MachineLearningConfig>(options =>
{
configuration.GetSection("MachineLearningConfig").bind(options);
});
}
}
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddAppsettingsFile(
this IConfigurationBuilder configurationBuilder,
FunctionsHostBuilderContext context,
bool useEnvironment = false
)
{
if (context == null) throw new ArgumentNullException(nameof(context));
var environmentSection = string.Empty;
if (useEnvironment)
{
environmentSection = $".{context.EnvironmentName}";
}
configurationBuilder.AddJsonFile(
path: Path.Combine(context.ApplicationRootPath, $"appsettings{environmentSection}.json"),
optional: true,
reloadOnChange: false);
return configurationBuilder;
}
}
}
Nkosi 的解决方案运行良好,但它确实通过替换 IConfiguration 单例:services.AddSingleton<IConfiguration>
来更新 Azure 函数运行时为自己加载设置的方式。
我更喜欢另一个没有注入的IConfigurationRoot。我只需要注入链接到我自己的 IConfigurationRoot 的设置IOption<MachineLearningSettings>
。
我构建了另一个 IConfigurationRoot,它是 Startup 类的成员:
public class Startup : FunctionsStartup
{
private IConfigurationRoot _functionConfig = null;
private IConfigurationRoot FunctionConfig( string appDir ) =>
_functionConfig ??= new ConfigurationBuilder()
.AddJsonFile(Path.Combine(appDir, "appsettings.json"), optional: true, reloadOnChange: true)
.Build();
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddOptions<MachineLearningSettings>()
.Configure<IOptions<ExecutionContextOptions>>((mlSettings, exeContext) =>
FunctionConfig(exeContext.Value.AppDirectory).GetSection("MachineLearningSettings").Bind(mlSettings) );
}
}
注意:连接字符串必须保留在应用程序设置中,因为触发器需要它来创建未启动的函数应用的实例(在消耗服务计划中(。
使用此.NET Core 3.1和Azure Function 3。花了几个小时天。这是我想到的。
[assembly: FunctionsStartup(typeof(Ugly.AzureFunctions.Startup))]
namespace Ugly.AzureFunctions
{
class Startup : FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
try
{
// On Azure, we need to get where the app is.
// If you use Directory.GetCurrentDirectory(), you will get something like D:Program Files (x86)SiteExtensionsFunctions3.0.1478532bit
var basePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..");
var environmentName = builder.GetContext().EnvironmentName;
builder.ConfigurationBuilder
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
}
catch (Exception ex)
{
// Handle exceptions about this. Which __should__ never ever happen.
// The previous comment is sarcastic.
throw;
}
}
public override void Configure(IFunctionsHostBuilder builder)
{
try
{
// DO NOT add the configuration as Singleton.
// If you need the IConfiguration:
//var configuration = builder.GetContext().Configuration;
builder.Services
.AddOptions<MachineLearningConfig>()
.Configure<IConfiguration>((settings, configuration) => {
configuration.GetSection("MachineLearningConfig").Bind(settings);
});
}
catch (Exception ex)
{
// Handle or not handle? That's the question.
throw;
}
}
}
}
在启动类中:
IConfigurationRoot config = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("someSettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
将 json 文件添加到包含设置的项目。请注意,local.settings.json 在部署过程中会被忽略/删除。(将文件命名为其他名称。
MS 文档已使用配置示例进行了更新
请记住安装先决条件部分中列出的必需库。
using System.IO;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
.AddEnvironmentVariables();
}
}
}
经过一番研究,我在 Githib 上遇到了这个线程
在函数应用中使用 appsettings.json + IConfiguration
我根据显示有效的评论和建议制作了以下扩展。
public static class FunctionHostBuilderExtensions {
/// <summary>
/// Set up the configuration for the builder itself. This replaces the
/// currently registered configuration with additional custom configuration.
/// This can be called multiple times and the results will be additive.
/// </summary>
public static IFunctionsHostBuilder ConfigureHostConfiguration (
this IFunctionsHostBuilder builder,
Action<IServiceProvider, IConfigurationBuilder> configureDelegate) {
IServiceCollection services = builder.Services;
var providers = new List<IConfigurationProvider>();
//Cache all current configuration provider
foreach (var descriptor in services.Where(d => d.ServiceType == typeof(IConfiguration)).ToList()) {
var existingConfiguration = descriptor.ImplementationInstance as IConfigurationRoot;
if (existingConfiguration is null) {
continue;
}
providers.AddRange(existingConfiguration.Providers);
services.Remove(descriptor);
}
//add new configuration based on original and newly added configuration
services.AddSingleton<IConfiguration>(sp => {
var configurationBuilder = new ConfigurationBuilder();
//call custom configuration
configureDelegate?.Invoke(sp, configurationBuilder);
providers.AddRange(configurationBuilder.Build().Providers);
return new ConfigurationRoot(providers);
});
return builder;
}
}
主要思想是提取所有当前注册的配置相关类型,创建新的构建器,应用自定义配置并构建新配置,并将原始和自定义配置详细信息合并为一个。
然后它将用于Startup
public class Startup : FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
builder.ConfigureHostConfiguration((sp, config) => {
var executioncontextoptions = sp.GetService<IOptions<ExecutionContextOptions>>().Value;
var currentDirectory = executioncontextoptions.AppDirectory;
config
.SetBasePath(currentDirectory)
.AddJsonFile("appSettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
//if there are multiple settings files, consider extracting the list,
//enumerating it and adding them to the configuration builder.
});
builder.Services
.AddOptions<MachineLearningConfig>()
.Configure<IConfiguration>((settings, configuration) => {
configuration.GetSection("MachineLearningConfig").Bind(settings);
});
}
}
以上内容现在应该能够从自定义配置中获取设置。
在本地开发函数应用时,必须在local.settings.json
项目文件中维护这些值的本地副本。若要了解详细信息,请参阅Local settings
文件。
将所需设置上传到 Azure 中的函数应用的最简单方法是使用成功发布项目后显示的"管理Application Settings...
"链接。
请参阅此example
,了解如何获取这些设置值。
var config = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
这是一个sample project