Files
sharp-basic/src/Sharp/Config
2025-11-13 19:10:09 +08:00
..
2025-11-13 19:10:09 +08:00
2025-11-13 19:03:48 +08:00
2025-11-13 19:10:09 +08:00
2025-11-13 18:43:23 +08:00
2025-11-13 18:43:23 +08:00
2025-11-13 19:10:09 +08:00
2025-11-13 18:43:23 +08:00
2025-11-13 18:43:23 +08:00
2025-11-13 19:10:09 +08:00
2025-11-13 19:10:09 +08:00

Config 模块

面向 .NET 的通用配置模块,聚合多种配置源(JSON / INI / XML / TOML / 环境变量),支持强类型绑定、数据注解验证、可选热更新、文件读写序列化,并提供对外扩展 Hook 以便与外部公共库(如 Common 模块)集成。

本模块仅包含在 src/Sharp/Config 目录内的实现,遵从“公共项目,可插拔”的目标:你可以直接使用,也可通过依赖注入或 Hook 接入整体项目体系。

功能清单

  • 多格式配置读取:
    • JSON / XML:使用 Microsoft 官方 provider,并支持 ReloadOnChange
    • INI / TOML:内置轻量解析,注入为内存配置源(当前默认不启用文件级热更新)
  • 多格式配置写入:支持将强类型对象写入为 JSON / XML / INI / TOML 文件
  • 强类型绑定:Get<T>() / TryGet<T>() 直接从 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。

使用方式

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<AppSettings>(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<AppSettings>("out/appsettings.json", ConfigFormat.Json);
var a2 = cm.LoadFromFile<AppSettings>("out/extra.xml", ConfigFormat.Xml);
var a3 = cm.LoadFromFile<AppSettings>("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<AppSettings>(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<ConfigManager>();

重要行为与限制

  • 配置源合并策略:后添加覆盖先添加(与官方一致)
  • INI 读取:
    • 将“根键(未在任何节中)”与“节内键”一起展平成键路径
    • 当前默认行为:将根键复制到所有已声明的一级节(若该节未定义同名键)。此举旨在方便简化配置,但可能与部分 INI 规范理解有差异;如需关闭或自定义,可在后续版本提供开关
    • INI 写入:支持简单类型与一层嵌套对象
  • TOML 读取:
    • 解析为键值对注入,默认不启用文件级热更新
    • 直接 LoadFromFile<T>(..., 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<T> 暂不支持 TOML 直接反序列化。
  4. 能否与 Common 模块集成?

    • 可以,通过 ConfigOptions.ConfigureBuilderHook 把 Common 的扩展注入到构建管线。

变更建议与后续计划

  • 提供选项开关以切换 INI 映射策略(是否复制根键到各节)
  • 为 INI/TOML 增加自定义 FileConfigurationProvider,支持 ReloadOnChange
  • 增强序列化器的集合/字典类型支持(当前对 INI 做了简化)