Skip to content

.NET 8 独立模式 Azure Function 加载 appsettings.json 配置

问题描述

当将 Azure Function 从 .NET 6 同进程模型迁移到 .NET 8 独立工作模式时,遇到 appsettings.json 配置文件在 Azure 环境中失效的问题。尽管本地开发环境运行正常,且已确认配置文件正确部署到输出目录,但在 Azure 生产环境中会出现以下情况:

  • 配置对象的所有属性均为 null
  • 仅通过 Azure 环境变量面板设置的配置才会生效
  • 无法加载包含嵌套对象或数组的复杂 JSON 配置

此问题产生的核心原因是:.NET 8 独立模式中默认的配置加载机制无法适应 Azure 环境的实际目录结构,导致框架无法定位到正确的配置文件路径。

解决方案

推荐方法:使用程序集位置设置基路径

通过获取程序集执行位置来动态解析配置文件的准确路径,这是一种跨平台兼容的方法:

csharp
// Program.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.Reflection;
using System.IO;

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .ConfigureAppConfiguration((hostingContext, configBuilder) =>
    {
        // 获取当前程序集路径作为配置基路径
        string assemblyPath = Assembly.GetExecutingAssembly().Location;
        string assemblyDir = Path.GetDirectoryName(assemblyPath) 
                            ?? Directory.GetCurrentDirectory();
        
        configBuilder
            .SetBasePath(assemblyDir) // 关键:使用程序集所在目录
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", 
                         optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

        // 仅在开发环境加载本地设置和用户机密
        if (hostingContext.HostingEnvironment.IsDevelopment())
        {
            configBuilder
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddUserSecrets<Program>(optional: true);
        }
    })
    .ConfigureServices((context, services) =>
    {
        // 选项绑定(示例)
        services.Configure<DummyOption>(
            context.Configuration.GetSection(nameof(DummyOption)));
    })
    .Build();

host.Run();

关键优化点

  • Assembly.GetExecutingAssembly().Location:准确获取程序集物理路径
  • 跨平台兼容:避免硬编码 Azure 特定路径(如 /home/site/wwwroot)
  • 环境感知:自动加载环境特定的配置文件(appsettings.Production.json 等)

项目文件配置 (.csproj)

确保配置文件被正确部署到输出目录:

xml
<!-- 添加在 ItemGroup 内部 -->
<None Update="host.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
<None Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.*.json"> <!-- 通配符加载所有环境配置 -->
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>

备选方案

当程序集位置方法不适用时,可考虑以下变通方案:

1. Azure Linux 环境专用路径(推荐仅用于临时方案)

csharp
.ConfigureAppConfiguration((hostingContext, configBuilder) =>
{
    string basePath = hostingContext.HostingEnvironment.IsDevelopment() 
        ? Directory.GetCurrentDirectory() 
        : "/home/site/wwwroot"; // Azure Linux 固定部署目录
    
    configBuilder
        .SetBasePath(basePath)
        .AddJsonFile("appsettings.json", optional: true);
})

2. 当前目录回退方案

csharp
.ConfigureAppConfiguration((hostingContext, configBuilder) =>
{
    configBuilder
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: true);
})

备选方案限制

  • Linux 路径方案
    仅适用于 Azure Linux 环境(Windows 主机无效),且依赖平台实现细节
    ⚠️ 未来 Azure 路径更改可能导致失效

  • 当前目录方案
    在 Consumption 计划中可能失效
    行为在 App Service 计划和 Consumption 计划间不一致

技术原理解析

核心问题根源

在 .NET 8 独立模式中,Azure Functions 主机和用户应用程序分别运行在不同的进程中:

  1. 主机进程:位于 /azure-functions-host
  2. 工作进程:位于 /home/site/wwwroot(应用实际部署位置)

框架默认尝试从主机进程目录加载配置,而非应用部署目录,导致 appsettings.json 文件找不到。

为什么程序集位置方案最优

  • 路径准确性:程序集位置始终指向实际部署目录
  • 环境一致性:本地开发与 Azure 生产环境行为一致
  • 兼容性保证

验证配置加载成功

在配置加载后添加诊断日志:

csharp
.ConfigureAppConfiguration((hostingContext, configBuilder) => 
{
    // 配置构建代码...
    
    //【诊断】打印所有加载的配置源
    var config = configBuilder.Build();
    hostingContext.Logging.LogInformation($"Loaded config providers: 
        {string.Join(", ", config.Providers.Select(p => p.ToString()))}");
        
    //【诊断】检查文件是否存在
    string targetPath = Path.Combine(assemblyDir, "appsettings.json");
    hostingContext.Logging.LogInformation($"Config file exists: {File.Exists(targetPath)}");
})

部署验证清单

  1. [ ] 在 .csproj 中配置所有配置文件复制到输出目录
  2. [ ] 使用 Kudu 控制台确认 Azure 上存在 appsettings.json
    /home/site/wwwroot> ls appsettings.json
  3. [ ] 检查应用日志中的诊断信息确保:
    • 配置提供程序包含 JsonConfigurationProvider
    • 文件存在性检查返回 True

通过本文提供的解决方案,可彻底解决 .NET 8 独立模式 Azure Function 的 appsettings.json 加载问题,同时保持配置系统的灵活性和跨环境一致性。