.NET Core 配置与选项

在现代 .NET Core 开发中,配置与选项系统早已脱离了传统 App.config 的单一模式,形成了一套支持多源混合、强类型绑定、动态更新、可高度扩展的完整体系。

1. 获取配置与选项

所有配置的读取、合并、管理都围绕 IConfiguration 对象的 Providers 集合展开,系统会把所有注册的配置源统一扁平化管理,对外提供一致的访问入口。

前置准备步骤

新建一个 .NET Core 控制台或 Web 项目,在项目根目录添加名为 appsettings.json 的文件。

右键点击该文件,选择"属性 ",将"复制到输出目录 "设置为"始终复制",避免运行时找不到配置文件。

通过 NuGet 包管理器安装两个核心依赖:Microsoft.Extensions.Configuration.Json 和 Microsoft.Extensions.Configuration.Binder。

编写基础构建代码,创建 ConfigurationBuilder 实例,注册 JSON 配置源并构建配置根对象:

cs 复制代码
using Microsoft.Extensions.Configuration;

// 1. 创建配置构建器
IConfigurationBuilder configBuilder = new ConfigurationBuilder();

// 2. 注册JSON配置源,reloadOnChange开启后文件修改自动重载
configBuilder.AddJsonFile(
path: "appsettings.json",
optional: false,
reloadOnChange: true
);

// 3. 构建配置根对象
IConfigurationRoot configRoot = configBuilder.Build();

IConfiguration 常用方法与属性列表

|------------------------|-------------------------|----------------------------------------------------|
| 成员名称 | 作用说明 | 代码示例 |
| thisstring key | 索引器,直接读写配置键值 | var siteName = configRoot"SiteName"; |
| GetSection(string key) | 获取指定层级的子配置节点,支持冒号分隔嵌套路径 | var dbSection = configRoot.GetSection("Database"); |
| GetChildren() | 获取当前节点下的所有子配置项 | foreach(var child in configRoot.GetChildren()) |
| GetReloadToken() | 获取配置变更通知令牌,用于自定义监听逻辑 | var token = configRoot.GetReloadToken(); |
| Providers | 获取所有已注册的配置提供器集合 | foreach(var provider in configRoot.Providers) |
| Reload() | 强制重新加载所有配置源的数据 | configRoot.Reload(); |

2. 使用强类型对象承载配置

直接通过字符串键读取配置很容易出现拼写错误、类型转换异常,而强类型绑定可以自动把配置映射到普通 POCO 类上,实现类型安全的配置访问。

步骤与代码

在 appsettings.json 中添加嵌套结构的测试配置:

bash 复制代码
{
"SiteName": "我的博客站点",
"MaxPageSize": 50,
"Database": {
"ConnectionString": "Server=localhost;Database=BlogDB;",
"Timeout": 30
}
}

定义对应结构的普通 POCO 类,不需要继承任何基类:

cs 复制代码
public class AppSettings
{
public string SiteName { get; set; }
public int MaxPageSize { get; set; }
public DatabaseSettings Database { get; set; }
}

public class DatabaseSettings
{
public string ConnectionString { get; set; }
public int Timeout { get; set; }
}

执行自动绑定,直接生成完整的强类型配置对象:

cs 复制代码
var appSettings = new AppSettings();
configRoot.GetSection("AppSettings").Bind(appSettings);

// 直接访问强类型属性,无需手动类型转换
Console.WriteLine($"站点名称:{appSettings.SiteName}");
Console.WriteLine($"数据库连接串:{appSettings.Database.ConnectionString}");

3. 使用环境变量配置源

环境变量是容器化、云原生部署中最常用的配置注入方式,可以把密钥、连接串等敏感配置和代码文件完全隔离,避免敏感信息泄露。

步骤与代码

首先通过 NuGet 安装 Microsoft.Extensions.Configuration.EnvironmentVariables 包。

在配置构建流程中添加环境变量配置源:

cs 复制代码
configBuilder.AddEnvironmentVariables();

测试嵌套配置:系统会自动把环境变量中的双下划线 __ 转换为配置层级的冒号,比如设置环境变量 AppSettings__SiteName="生产环境站点",程序读取时就会自动映射为 AppSettings:SiteName 配置项,环境变量的优先级默认高于 JSON 文件,会自动覆盖本地配置。

4. 使用命令行配置源

命令行配置源适合在应用启动时临时覆盖配置,不需要修改任何配置文件,非常适合容器启动脚本、临时调试场景。

步骤与代码

安装 NuGet 包 Microsoft.Extensions.Configuration.CommandLine。

在配置构建流程中添加命令行配置源,同时支持自定义短参数映射:

cs 复制代码
// 定义短参数映射,比如 -p 对应 --Port
var switchMappings = new Dictionary<string, string>()
{
["-p"] = "Port",
["-e"] = "Environment"
};
configBuilder.AddCommandLine(args, switchMappings);

启动应用时传入参数即可覆盖配置,示例启动命令:

MyApp.exe --Port 8080 --Environment Production

也可以使用短参数

MyApp.exe -p 8080 -e Production

5. 配置管理器 ConfigurationManager

从 .NET 6 开始新增的 ConfigurationManager 把配置构建、服务托管、环境管理能力整合在一起,大幅简化了应用启动流程,是现代 .NET 项目的默认配置方式。

在默认创建的 .NET 6+ Web 项目中,你不需要手动创建 ConfigurationBuilder,框架已经预初始化好了 ConfigurationManager:

cs 复制代码
var builder = WebApplication.CreateBuilder(args);
// 这里的 builder.Configuration 就是 ConfigurationManager 实例
// 它已经自动按优先级加载了:appsettings.json → appsettings.{环境}.json → 环境变量 → 命令行
var siteName = builder.Configuration["AppSettings:SiteName"];

// 你可以继续添加自定义配置源
builder.Configuration.AddJsonFile("customConfig.json", optional: true);

6. 选项模式

选项模式是 .NET Core 官方推荐的最佳实践,通过依赖注入把配置托管到服务容器中,实现配置与业务逻辑的完全解耦。

6.1 将配置绑定Options对象

先在项目中引入依赖注入相关包,在 Program.cs 中完成注册:

cs 复制代码
builder.Services.Configure<AppSettings>(
builder.Configuration.GetSection("AppSettings")
);

在业务服务中通过依赖注入获取选项对象:

cs 复制代码
public class BlogService
{
private readonly AppSettings _settings;

public BlogService(IOptions<AppSettings> settings)
{
_settings = settings.Value;
}

public void PrintConfig()
{
Console.WriteLine(_settings.SiteName);
}
}

6.2 命名选项的使用

当需要同一配置类的多组不同配置时,命名选项可以避免重复定义相似的配置类:

cs 复制代码
// 注册两组不同名称的数据库配置
builder.Services.Configure<DatabaseSettings>("Primary",
builder.Configuration.GetSection("Database:Primary"));
builder.Services.Configure<DatabaseSettings>("Replica",
builder.Configuration.GetSection("Database:Replica"));

// 使用时通过名称获取指定配置
public class DbFactoryService
{
public DbFactoryService(IOptionsSnapshot<DatabaseSettings> dbOptions)
{
var primaryDb = dbOptions.Get("Primary");
var replicaDb = dbOptions.Get("Replica");
}
}

6.3 Options内容验证

在应用启动阶段就自动验证配置合法性,避免运行时才发现配置错误导致崩溃:

cs 复制代码
// 先给选项类添加数据注解特性
public class AppSettings
{
[Required(ErrorMessage = "站点名称不能为空")]
public string SiteName { get; set; }

[Range(10, 100, ErrorMessage = "分页大小必须在10-100之间")]
public int MaxPageSize { get; set; }
}

// 注册选项时开启数据注解验证
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"))
.ValidateDataAnnotations();

6.4 Options后期配置

后期配置会在所有配置源加载完成后最后执行,适合补充默认值、联动调整配置:

cs 复制代码
builder.Services.PostConfigure<AppSettings>(options =>
{
// 所有配置源加载完成后,自动给站点名称加上环境后缀
options.SiteName += $" - {builder.Environment.EnvironmentName}";
});

6.5 IOptionsSnapshot

Scoped 生命周期的选项快照,支持配置热更新,同一个请求内配置保持一致,新请求自动读取最新配置:

cs 复制代码
public class HomeController : Controller
{
private readonly IOptionsSnapshot<AppSettings> _snapshotSettings;

public HomeController(IOptionsSnapshot<AppSettings> snapshotSettings)
{
_snapshotSettings = snapshotSettings;
}

public IActionResult Index()
{
// 修改appsettings.json后,刷新页面就能拿到新配置,无需重启应用
return Content(_snapshotSettings.Value.SiteName);
}
}

6.6 IOptionsMonitor

单例生命周期的配置监控器,可以注册回调监听配置变更事件:

cs 复制代码
var app = builder.Build();
var monitor = app.Services.GetRequiredService<IOptionsMonitor<AppSettings>>();
// 配置变化时自动触发回调
monitor.OnChange(newSettings =>
{
Console.WriteLine($"配置已更新,新站点名称:{newSettings.SiteName}");
});

7. 设置一个配置中心

当应用规模扩展到分布式集群时,分散在每台机器的配置文件维护成本极高,自定义配置中心可以实现配置的集中管理和实时推送。

7.1 什么是配置中心

配置中心是独立于业务应用的集中式配置服务,所有应用实例统一从服务端拉取配置,支持配置的集中修改、版本回溯、灰度发布,修改一次配置就能同步到所有节点,无需逐个登录服务器修改文件。

7.2 ConfigurationSource

自定义配置源类,用来承载配置中心的连接参数:

cs 复制代码
public class CustomConfigSource : IConfigurationSource
{
public string ServerUrl { get; set; }
public string AppId { get; set; }

public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new CustomConfigProvider(this);
}
}

7.3 ConfigurationProvider

核心配置提供器,负责从远程配置中心拉取数据:

cs 复制代码
public class CustomConfigProvider : ConfigurationProvider
{
private readonly CustomConfigSource _source;
public CustomConfigProvider(CustomConfigSource source)
{
_source = source;
}

// 重写Load方法,从远程接口拉取配置
public override void Load()
{
using var httpClient = new HttpClient();
var json = httpClient.GetStringAsync($"{_source.ServerUrl}/pull?appId={_source.AppId}").Result;
Data = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
}
}

7.4 配置扩展方法

封装扩展方法,让自定义配置源接入更简洁:

cs 复制代码
public static class CustomConfigExtensions
{
    public static IConfigurationBuilder AddCustomConfigCenter(
this IConfigurationBuilder builder,
string serverUrl,
string appId)
{
return builder.Add(new CustomConfigSource
{
ServerUrl = serverUrl,
AppId = appId
});
}
}

接入时只需要一行代码:

cs 复制代码
configBuilder.AddCustomConfigCenter("http://localhost:5000/config", "MyBlogApp");

7.5 配置动态更新(HTTP Long Polling方案)

在配置提供器中加入长轮询逻辑,实现配置准实时推送,这也是 Nacos、Apollo 等主流配置中心的核心实现方案:

cs 复制代码
public class CustomConfigProvider : ConfigurationProvider
{
private Timer _pollTimer;
private string _currentVersion = "0";

public CustomConfigProvider(CustomConfigSource source)
{
_source = source;
// 后台启动长轮询任务
_pollTimer = new Timer(StartLongPolling, null, 0, Timeout.Infinite);
}

private async void StartLongPolling(object state)
{
using var httpClient = new HttpClient();
// 设置90秒长轮询超时,和主流配置中心默认超时保持一致
httpClient.Timeout = TimeSpan.FromSeconds(90);

while(true)
{
try
{
// 带着当前版本号发起请求,服务端没有新配置就挂起连接
var response = await httpClient.GetAsync(
$"{_source.ServerUrl}/longpoll?appId={_source.AppId}&version={_currentVersion}"
);

if(response.IsSuccessStatusCode)
{
var configData = await response.Content.ReadFromJsonAsync<ConfigResponse>();
// 配置有更新,重新加载
Data = configData.Configs;
_currentVersion = configData.Version;
// 触发配置系统重载通知
OnReload();
}
}
catch(Exception ex)
{
// 长轮询超时或出错,等待1秒后重试
await Task.Delay(1000);
}
}
}
}

服务端侧的长轮询核心逻辑可以借助 .NET Core 的 Channel 实现,配置变更时直接通知等待的请求,无需轮询数据库,实现准实时推送效果。

相关推荐
搭贝1 小时前
【无标题】
人工智能·低代码·ai·1024程序员节·搭贝
mudtools1 小时前
在飞书 SDK 上用 Microsoft Agent Framework 搭一个能干活的工作搭子
人工智能·ai·.net·飞书·agent
ChaITSimpleLove1 小时前
Layout.WatchDog.NET 1.5.0-beta.3 发布:统一全后端雪花 ID 策略,数据库能力矩阵一览
.net·watchdog·日志记录器·节点资源监控看板·.net专属轻量级日志中心
IT规划师2 小时前
MQTT 实战 - 放下炫技,8 年踩坑的真心话与方法论(收官)
物联网·.net
CSharp精选营2 小时前
.NET 接口从 800ms 干到 60ms:生产环境这几个优化点最关键
性能优化·c#·.net·efcore
云和数据.ChenGuang10 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
天空依然蔚蓝11 小时前
分享一个自动检测网站在线状态的python程序
python·运维开发·1024程序员节
路灯上の马陈11 小时前
二叉树的定义与性质
数据结构·二叉树·树结构·1024程序员节
中伟视界16 小时前
煤矿安全监控数据造假识别:时序异常检测与AI视频交叉核验工程实践
1024程序员节