# Config 模块 面向 .NET 的通用配置模块,聚合多种配置源(JSON / INI / XML / TOML / 环境变量),支持强类型绑定、数据注解验证、可选热更新、文件读写序列化,并提供对外扩展 Hook 以便与外部公共库(如 Common 模块)集成。 本模块仅包含在 `src/Sharp/Config` 目录内的实现,遵从“公共项目,可插拔”的目标:你可以直接使用,也可通过依赖注入或 Hook 接入整体项目体系。 ## 功能清单 - 多格式配置读取: - JSON / XML:使用 Microsoft 官方 provider,并支持 `ReloadOnChange` - INI / TOML:内置轻量解析,注入为内存配置源(当前默认不启用文件级热更新) - 多格式配置写入:支持将强类型对象写入为 JSON / XML / INI / TOML 文件 - 强类型绑定:`Get()` / `TryGet()` 直接从 IConfiguration 绑定到 POCO - 数据注解验证:`[Required]` / `[Range]` 等;支持递归验证嵌套对象与集合 - 环境变量合并:可设置前缀,合并到配置树(优先级最高) - 热更新(可选):JSON / XML 支持文件监控自动重载;回调 `OnReload` - 可扩展: - `ConfigOptions.ConfigureBuilderHook`:注入外部对 `IConfigurationBuilder` 的自定义构建(可与 Common 模块对接) - `AddConfig(IServiceCollection, ...)`:便捷注册到 DI 容器 ## 架构与设计 模块核心集中在以下类: - `ConfigManager`:构建配置、获取/验证、保存、热更新回调 - `ConfigOptions`:配置构建选项(BasePath、Sources、ReloadOnChange、Env 前缀、Hook 等) - `ConfigSource`:单个配置源描述(路径、格式、是否可选) - `ConfigBuilder`:便捷构建器(从文件列表快速构建) - 序列化器: - `Serialization/JsonConfigSerializer` - `Serialization/XmlConfigSerializer` - `Serialization/IniConfigSerializer` - `Serialization/TomlConfigSerializer` 注意:当前 INI / TOML 的读取在构建期解析为键值对注入,不启用文件级热更新;JSON / XML 使用官方 provider,支持 `ReloadOnChange`。 ## 使用方式 ### 0) 约定优于配置的快速接入 ``` // 1 行代码拿到强类型配置,并做数据注解验证 var settings = ConfigDefaults.Load(); // 或者先构建 ConfigManager,方便后续使用 IConfiguration var manager = ConfigDefaults.BuildDefault(opt => { opt.EnvironmentPrefix = "APP"; // 仅读取 APP__ 前缀的环境变量 opt.WithIni("extra.ini") // 同时加载 INI .WithToml("feature.toml"); // 以及 TOML }); var settings = manager.Get(validate: true); ``` 依赖注入中也可以一步完成“构建+绑定+热更新”: ``` services.AddDefaultConfigBinding(defaults => { defaults.EnvironmentName = "Production"; // 手动指定环境 }, section: "App", onReload: s => logger.LogInformation("配置已更新")); ``` ### 1) 从文件构建(常见) ``` var cm = Config.ConfigBuilder.BuildFromFiles( files: new[] { "appsettings.json", "settings.ini", "extra.xml", "feature.toml" }, reloadOnChange: true, // JSON/XML 支持 includeEnvironment: true, // 合并环境变量 environmentPrefix: "APP_", // 推荐:无双下划线的短前缀,如 "APP" basePath: AppContext.BaseDirectory // 或自定义目录 ); ``` 环境变量键:使用双下划线分隔层级,例如 `APP__Database__Host`。如果设置了 `environmentPrefix: "APP_"`,也兼容 `APP__Name` 这类书写,但推荐前缀不以 `_` 结尾,例如 `"APP"`。 ### 2) 强类型绑定与验证 ``` public class AppSettings { public string? Name { get; set; } [Range(1,65535)] public int Port { get; set; } [Required] public string? RequiredField { get; set; } public DbSettings Database { get; set; } = new(); } public class DbSettings { [Required] public string? Host { get; set; } } var settings = cm.Get(validate: true); ``` 验证失败会抛出 `ConfigurationValidationException`,异常中包含 `Errors` 详细列表。 ### 3) 序列化写入与直接读取文件 ``` // 保存为 JSON / XML / INI / TOML cm.Save(settings, "out/appsettings.json", ConfigFormat.Json); cm.Save(settings, "out/extra.xml", ConfigFormat.Xml); cm.Save(settings, "out/settings.ini", ConfigFormat.Ini); cm.Save(settings, "out/feature.toml", ConfigFormat.Toml); // 从文件读取到强类型对象(JSON/XML/INI 支持;TOML 建议用配置管线绑定) var a1 = cm.LoadFromFile("out/appsettings.json", ConfigFormat.Json); var a2 = cm.LoadFromFile("out/extra.xml", ConfigFormat.Xml); var a3 = cm.LoadFromFile("out/settings.ini", ConfigFormat.Ini); // TOML:直接泛型反序列化暂不支持(抛 NotSupportedException) // 建议:通过 AddTomlFile(若集成第三方)或当前内置的 TOML 解析+绑定路径获取强类型 ``` ### 4) 环境变量合并 ``` var cm = Config.ConfigBuilder.BuildFromFiles( new[] { "appsettings.json" }, includeEnvironment: true, environmentPrefix: "APP" // 推荐不带下划线 ); // Windows / Linux 环境变量示例: // APP__Name=fromEnv // APP__Database__Host=envHost ``` ### 5) 热更新与回调 ``` // 仅 JSON / XML 文件通过官方 provider 支持重载 using var sub = cm.OnReload(() => { var newSettings = cm.Get(validate: true); // TODO: 应用变更 }); ``` 如需对 INI / TOML 也启用热更新,可在后续迭代提供自定义 `FileConfigurationProvider` 实现(欢迎按需添加)。 ### 6) Hook 扩展(对接 Common 模块) ``` var cm = Config.ConfigBuilder.Build(opt => { opt.BasePath = AppContext.BaseDirectory; opt.Sources.Add(new ConfigSource { Path = "appsettings.json", Format = ConfigFormat.Json, Optional = true }); opt.ConfigureBuilderHook = builder => { // 在此对 IConfigurationBuilder 做进一步处理 // 例如:调用 Common 模块的扩展方法 // builder = Common.ConfigExtensions.AddCommonSources(builder); return builder; }; }); ``` ### 7) 依赖注入 ``` using Microsoft.Extensions.DependencyInjection; var services = new ServiceCollection(); services.AddConfig(opt => { opt.BasePath = AppContext.BaseDirectory; opt.Sources.Add(new ConfigSource { Path = "appsettings.json", Format = ConfigFormat.Json }); }); var provider = services.BuildServiceProvider(); var cm = provider.GetRequiredService(); ``` ## 重要行为与限制 - 配置源合并策略:后添加覆盖先添加(与官方一致) - INI 读取: - 将“根键(未在任何节中)”与“节内键”一起展平成键路径 - 当前默认行为:将根键复制到所有已声明的一级节(若该节未定义同名键)。此举旨在方便简化配置,但可能与部分 INI 规范理解有差异;如需关闭或自定义,可在后续版本提供开关 - INI 写入:支持简单类型与一层嵌套对象 - TOML 读取: - 解析为键值对注入,默认不启用文件级热更新 - 直接 `LoadFromFile(..., Toml)` 暂不支持;请通过配置管线绑定获取强类型 - 环境变量前缀: - 推荐使用不带下划线的短前缀(如 `APP`)+ 双下划线分隔层级(`APP__Section__Key`) - 模块对 `"APP_"` 这类带下划线结尾的前缀做了兼容处理,但仍建议遵循推荐写法 - 验证: - 使用 DataAnnotations,递归验证嵌套对象与集合 - 性能: - INI/TOML 读取为一次性解析(非流式);适合小中等规模配置。大型配置建议 JSON/XML ## 常见问题(FAQ) 1. 为什么 INI/TOML 没有热更新? - 当前实现为轻量解析注入内存,未挂载文件监控。JSON/XML 已可直接使用热更新。若需要对 INI/TOML 启用热更新,可按 `FileConfigurationProvider` 模式扩展(可在后续版本提供)。 2. 环境变量用法推荐? - 使用 `environmentPrefix: "APP"`,并设置 `APP__Section__Key`。尽量避免在前缀尾部加 `_`,可减少歧义。 3. TOML 的写入后如何强类型读取? - 通过配置构建(Add... + 绑定)获取强类型对象;`LoadFromFile` 暂不支持 TOML 直接反序列化。 4. 能否与 Common 模块集成? - 可以,通过 `ConfigOptions.ConfigureBuilderHook` 把 Common 的扩展注入到构建管线。 ## 变更建议与后续计划 - 提供选项开关以切换 INI 映射策略(是否复制根键到各节) - 为 INI/TOML 增加自定义 `FileConfigurationProvider`,支持 `ReloadOnChange` - 增强序列化器的集合/字典类型支持(当前对 INI 做了简化)