ASP.NET Core中如何将环境配置文件的数组追加到主配置文件?
在ASP.NET Core中合并Serilog配置的WriteTo数组(主配置+环境配置)
默认情况下,ASP.NET Core的配置系统处理数组时是替换逻辑——也就是说,环境配置里的Serilog:WriteTo数组会直接覆盖主配置中的对应数组,而不是追加。要实现你要的“把环境配置的WriteTo项追加到主配置数组”的需求,有两种实用的方案,我给你详细说说:
方案一:手动在Program.cs中合并配置(快速上手)
这种方式适合简单场景,直接在启动代码里手动加载并合并两个配置文件的WriteTo数组:
var builder = WebApplication.CreateBuilder(args); // 先单独加载主配置和环境配置 var mainConfig = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); var envConfig = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json") .Build(); // 取出主配置的WriteTo数组(为空就初始化新列表) var mainWriteTo = mainConfig.GetSection("Serilog:WriteTo") .Get<List<Dictionary<string, object>>>() ?? new List<Dictionary<string, object>>(); // 取出环境配置的WriteTo数组(为空就跳过) var envWriteTo = envConfig.GetSection("Serilog:WriteTo") .Get<List<Dictionary<string, object>>>() ?? new List<Dictionary<string, object>>(); // 把环境配置的项追加到主数组里 mainWriteTo.AddRange(envWriteTo); // 将合并后的数组绑定回builder的配置中 builder.Configuration.GetSection("Serilog:WriteTo").Value = null; // 先清空原有配置 builder.Configuration.GetSection("Serilog:WriteTo").Bind(mainWriteTo); // 正常配置Serilog builder.Host.UseSerilog((context, config) => { config.ReadFrom.Configuration(context.Configuration); }); var app = builder.Build(); // 后续中间件逻辑... app.Run();
这段代码的思路很直接:先分别拿到主配置和环境配置里的WriteTo列表,合并后再更新到应用的配置系统中,这样Serilog读取的时候就是合并后的完整数组了。
方案二:自定义配置提供者(优雅复用)
如果你的项目需要多次处理数组追加的场景,或者想让配置逻辑更整洁,可以写一个自定义的配置提供者,专门处理数组的追加逻辑:
首先创建自定义的配置源和提供者:
public class AppendArrayConfigurationSource : IConfigurationSource { private readonly IConfigurationSource _source; private readonly string _targetArrayPath; public AppendArrayConfigurationSource(IConfigurationSource source, string targetArrayPath) { _source = source; _targetArrayPath = targetArrayPath; } public IConfigurationProvider Build(IConfigurationBuilder builder) { var innerProvider = _source.Build(builder); return new AppendArrayConfigurationProvider(innerProvider, _targetArrayPath); } } public class AppendArrayConfigurationProvider : ConfigurationProvider { private readonly IConfigurationProvider _innerProvider; private readonly string _targetArrayPath; public AppendArrayConfigurationProvider(IConfigurationProvider innerProvider, string targetArrayPath) { _innerProvider = innerProvider; _targetArrayPath = targetArrayPath; } public override void Load() { _innerProvider.Load(); // 获取主配置中已有的目标数组项 var existingArrayEntries = Data .Where(kv => kv.Key.StartsWith($"{_targetArrayPath}:")) .ToDictionary(kv => kv.Key, kv => kv.Value); // 获取环境配置中的目标数组项 var newArrayEntries = _innerProvider.Data .Where(kv => kv.Key.StartsWith($"{_targetArrayPath}:")) .ToList(); if (existingArrayEntries.Any()) { // 计算现有数组的最大索引,确保新项追加在后面 var maxExistingIndex = existingArrayEntries.Keys .Select(k => int.Parse(k.Split(':')[1])) .Max(); foreach (var entry in newArrayEntries) { var keyParts = entry.Key.Split(':'); var newIndex = maxExistingIndex + 1 + int.Parse(keyParts[1]); var newKey = $"{_targetArrayPath}:{newIndex}:{string.Join(':', keyParts.Skip(2))}"; Data[newKey] = entry.Value; } } else { // 如果主配置里没有数组项,直接添加环境配置的项 foreach (var entry in newArrayEntries) { Data[entry.Key] = entry.Value; } } } } // 写个扩展方法方便调用 public static class ConfigurationBuilderExtensions { public static IConfigurationBuilder AddJsonFileWithArrayAppend( this IConfigurationBuilder builder, string filePath, string targetArrayPath) { var jsonSource = new JsonConfigurationSource { Path = filePath, Optional = true, ReloadOnChange = false }; builder.Add(new AppendArrayConfigurationSource(jsonSource, targetArrayPath)); return builder; } }
然后在Program.cs里这样使用:
var builder = WebApplication.CreateBuilder(args); // 加载主配置,再加载环境配置并指定要追加的数组路径 builder.Configuration .AddJsonFile("appsettings.json") .AddJsonFileWithArrayAppend( $"appsettings.{builder.Environment.EnvironmentName}.json", "Serilog:WriteTo"); // 配置Serilog builder.Host.UseSerilog((context, config) => { config.ReadFrom.Configuration(context.Configuration); }); var app = builder.Build(); app.Run();
这个方案的好处是把数组追加的逻辑封装起来了,以后其他地方需要类似的数组合并,直接调用扩展方法就行,代码更干净。
额外提示
如果你的场景只是要给Serilog多添加一个Sink(比如环境配置里的ApplicationInsightsTraces),也可以不用合并配置,直接在代码里单独添加:
builder.Host.UseSerilog((context, config) => { config.ReadFrom.Configuration(context.Configuration) .WriteTo.ApplicationInsightsTraces("XXXXXXXX"); // 直接在这里添加环境特定的Sink });
不过这种方式不如配置合并灵活,适合单个Sink的简单场景。
内容的提问来源于stack exchange,提问作者Justin Dearing
相关产品推荐
相关产品推荐

