.NET 6控制台应用:同环境不同服务器加载指定appsettings异常
我有一个运行在多台服务器上的.NET 6控制台应用,用于监控服务器上的应用。这些服务器已为其他应用设置好DOTNET_ENVIRONMENT环境变量,具体如下:
Server A -> DOTNET_ENVIRONMENT = dev Server B -> DOTNET_ENVIRONMENT = qa Server C -> DOTNET_ENVIRONMENT = prod Server D -> DOTNET_ENVIRONMENT = prod
尽管Server C和D均为prod环境,但因监控的应用不同,需使用不同的appsettings,单一appsettings.prod.json无法满足需求,且我不能修改现有DOTNET_ENVIRONMENT的值,避免影响其他应用。
已执行操作
- 按服务器名称命名控制台应用的appsettings.json文件,例如创建
appsettings.ServerA.json、appsettings.ServerC.json等对应文件。 - 添加launchsettings配置文件,通过环境变量传递机器名用于测试:
{ "profiles": { "AppStatusWatchDog (ServerA)": { "commandName": "Project", "environmentVariables": { "MachineName": "ServerA" }, "dotnetRunMessages": "true" } } }
- 尝试在启动时加载正确的appsettings:
public static void Main(string[] args) { var pathToExe = Process.GetCurrentProcess().MainModule.FileName; var pathToContentRoot = Path.GetDirectoryName(pathToExe); // 本地测试时通过launch profile传入MachineName,生产环境直接用Environment.MachineName var machineName = Environment.GetEnvironmentVariable("MachineName") ?? Environment.MachineName; var config = new ConfigurationBuilder() .SetBasePath(pathToContentRoot) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{machineName}.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); // 后续代码... }
遇到的问题
手动构建的config能正确加载appsettings.json、对应机器的appsettings.{machineName}.json及环境变量,但通过依赖注入将IConfiguration注入到类的构造函数时,仅能加载appsettings.json和环境变量,无法加载对应机器的appsettings.{machineName}.json。
问题原因
.NET 6控制台应用中,Host.CreateDefaultBuilder(args)会自动构建一个默认的IConfiguration实例并注册到依赖注入容器中,这个默认配置只会加载:
appsettings.jsonappsettings.{DOTNET_ENVIRONMENT}.json- 环境变量
你手动构建的config并没有替换掉这个默认的配置实例,所以依赖注入时获取到的是默认的IConfiguration,自然不会包含按机器名命名的配置文件内容。
解决方法
需要将按机器名加载配置的逻辑整合到Host的配置流程中,确保DI容器中的IConfiguration包含你需要的所有配置源。
方法一:在HostBuilder中直接配置
修改Main方法,在创建Host时添加机器名对应的配置文件:
public static void Main(string[] args) { var host = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, configBuilder) => { var pathToExe = Process.GetCurrentProcess().MainModule.FileName; var pathToContentRoot = Path.GetDirectoryName(pathToExe); var machineName = Environment.GetEnvironmentVariable("MachineName") ?? Environment.MachineName; // 若想完全自定义配置源可清除默认项,否则直接添加新配置源即可 // configBuilder.Sources.Clear(); configBuilder.SetBasePath(pathToContentRoot) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{machineName}.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); }) .ConfigureServices((context, services) => { // 注册你的服务,此时注入的IConfiguration已包含机器名对应的配置 services.AddTransient<YourMonitoringService>(); }) .Build(); // 运行应用逻辑 var service = host.Services.GetRequiredService<YourMonitoringService>(); service.RunMonitoring(); host.Run(); }
方法二:将手动构建的config传入HostBuilder
如果希望保留手动构建config的逻辑,可以将其传入HostBuilder替换默认配置:
public static void Main(string[] args) { var pathToExe = Process.GetCurrentProcess().MainModule.FileName; var pathToContentRoot = Path.GetDirectoryName(pathToExe); var machineName = Environment.GetEnvironmentVariable("MachineName") ?? Environment.MachineName; var config = new ConfigurationBuilder() .SetBasePath(pathToContentRoot) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.{machineName}.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); var host = Host.CreateDefaultBuilder(args) .ConfigureHostConfiguration(builder => { // 将自定义config添加到Host配置中 builder.AddConfiguration(config); }) .ConfigureServices(services => { services.AddTransient<YourMonitoringService>(); }) .Build(); // 运行应用 var service = host.Services.GetRequiredService<YourMonitoringService>(); service.RunMonitoring(); host.Run(); }
注意事项
- 确保
appsettings.{machineName}.json文件的复制到输出目录属性设置为始终复制或如果较新则复制,否则部署到服务器后会找不到配置文件。 - 本地测试时,launchsettings中的
MachineName环境变量会覆盖Environment.MachineName,确保测试配置正确。
内容的提问来源于stack exchange,提问作者Ash K

