using System.Text; using Apq.Cfg; using Apq.Cfg.Changes; using Apq.Cfg.Ini; using Apq.Cfg.Xml; using Apq.Cfg.Yaml; using Apq.Cfg.Toml; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; Console.WriteLine("╔══════════════════════════════════════════════════════════════╗"); Console.WriteLine("║ Apq.Cfg 完整功能示例 ║"); Console.WriteLine("╚══════════════════════════════════════════════════════════════╝\n"); var baseDir = AppContext.BaseDirectory; // ============================================================================ // 示例 1: 基础用法 - JSON 配置与层级覆盖 // ============================================================================ await Demo1_BasicUsage(baseDir); // ============================================================================ // 示例 2: 多格式支持 - INI、XML、YAML、TOML // ============================================================================ await Demo2_MultiFormat(baseDir); // ============================================================================ // 示例 3: 配置节 (GetSection) 与子键枚举 // ============================================================================ await Demo3_ConfigSection(baseDir); // ============================================================================ // 示例 4: 批量操作 - GetMany / SetMany // ============================================================================ await Demo4_BatchOperations(baseDir); // ============================================================================ // 示例 5: 类型转换 // ============================================================================ await Demo5_TypeConversion(baseDir); // ============================================================================ // 示例 6: 动态配置重载 // ============================================================================ await Demo6_DynamicReload(baseDir); // ============================================================================ // 示例 7: 依赖注入集成 // ============================================================================ await Demo7_DependencyInjection(baseDir); // ============================================================================ // 示例 8: 编码映射配置 // ============================================================================ await Demo8_EncodingMapping(baseDir); Console.WriteLine("\n╔══════════════════════════════════════════════════════════════╗"); Console.WriteLine("║ 所有示例执行完成 ║"); Console.WriteLine("╚══════════════════════════════════════════════════════════════╝"); // ============================================================================ // 示例实现 // ============================================================================ static async Task Demo1_BasicUsage(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 1: 基础用法 - JSON 配置与层级覆盖"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); var configPath = Path.Combine(baseDir, "config.json"); var localConfigPath = Path.Combine(baseDir, "config.local.json"); // 创建基础配置 File.WriteAllText(configPath, """ { "App": { "Name": "MyApp", "Version": "1.0.0", "Debug": false }, "Database": { "Host": "localhost", "Port": 3306, "Name": "mydb" } } """); // 创建本地覆盖配置(高优先级) File.WriteAllText(localConfigPath, """ { "App": { "Debug": true }, "Database": { "Host": "192.168.1.100" } } """); // 构建配置:level 越大优先级越高 // 注意:环境变量不可写,所以 isPrimaryWriter 设置在 JSON 配置源上 var cfg = new CfgBuilder() .AddJson(configPath, level: 0, writeable: false) .AddJson(localConfigPath, level: 1, writeable: true, isPrimaryWriter: true) .AddEnvironmentVariables(level: 2, prefix: "MYAPP_") .Build(); // 读取配置 Console.WriteLine("1.1 读取配置值:"); Console.WriteLine($" App:Name = {cfg.Get("App:Name")}"); Console.WriteLine($" App:Version = {cfg.Get("App:Version")}"); Console.WriteLine($" App:Debug = {cfg.Get("App:Debug")} (被本地配置覆盖为 true)"); Console.WriteLine($" Database:Host = {cfg.Get("Database:Host")} (被本地配置覆盖)"); Console.WriteLine($" Database:Port = {cfg.Get("Database:Port")}"); // 检查配置是否存在 Console.WriteLine("\n1.2 检查配置是否存在:"); Console.WriteLine($" Exists(App:Name) = {cfg.Exists("App:Name")}"); Console.WriteLine($" Exists(NotExist:Key) = {cfg.Exists("NotExist:Key")}"); // 修改配置(写入到 isPrimaryWriter 的配置源,需要指定 targetLevel) Console.WriteLine("\n1.3 修改配置:"); cfg.Set("App:LastRun", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), targetLevel: 1); await cfg.SaveAsync(targetLevel: 1); Console.WriteLine($" 已设置 App:LastRun = {cfg.Get("App:LastRun")}"); // 删除配置 Console.WriteLine("\n1.4 删除配置:"); cfg.Set("App:TempKey", "临时值", targetLevel: 1); Console.WriteLine($" 设置 App:TempKey = {cfg.Get("App:TempKey")}"); cfg.Remove("App:TempKey", targetLevel: 1); await cfg.SaveAsync(targetLevel: 1); Console.WriteLine($" 删除后 App:TempKey = {cfg.Get("App:TempKey") ?? "(null)"}"); // 转换为 Microsoft.Extensions.Configuration Console.WriteLine("\n1.5 转换为 IConfigurationRoot:"); var msConfig = cfg.ToMicrosoftConfiguration(); Console.WriteLine($" msConfig[\"App:Name\"] = {msConfig["App:Name"]}"); cfg.Dispose(); File.Delete(configPath); File.Delete(localConfigPath); Console.WriteLine("\n[示例 1 完成]\n"); } static async Task Demo2_MultiFormat(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 2: 多格式支持 - INI、XML、YAML、TOML"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); // INI 格式 var iniPath = Path.Combine(baseDir, "config.ini"); File.WriteAllText(iniPath, """ [App] Name=IniApp Version=2.0.0 [Database] Host=ini-server Port=5432 """); // XML 格式 var xmlPath = Path.Combine(baseDir, "config.xml"); File.WriteAllText(xmlPath, """ XmlApp 3.0.0 xml-server 1433 """); // YAML 格式 var yamlPath = Path.Combine(baseDir, "config.yaml"); File.WriteAllText(yamlPath, """ App: Name: YamlApp Version: 4.0.0 Database: Host: yaml-server Port: 27017 """); // TOML 格式 var tomlPath = Path.Combine(baseDir, "config.toml"); File.WriteAllText(tomlPath, """ [App] Name = "TomlApp" Version = "5.0.0" [Database] Host = "toml-server" Port = 6379 """); // 分别测试各格式 Console.WriteLine("2.1 INI 格式:"); using (var iniCfg = new CfgBuilder().AddIni(iniPath, level: 0, writeable: true).Build()) { Console.WriteLine($" App:Name = {iniCfg.Get("App:Name")}"); Console.WriteLine($" Database:Port = {iniCfg.Get("Database:Port")}"); } Console.WriteLine("\n2.2 XML 格式:"); using (var xmlCfg = new CfgBuilder().AddXml(xmlPath, level: 0, writeable: true).Build()) { Console.WriteLine($" App:Name = {xmlCfg.Get("App:Name")}"); Console.WriteLine($" Database:Port = {xmlCfg.Get("Database:Port")}"); } Console.WriteLine("\n2.3 YAML 格式:"); using (var yamlCfg = new CfgBuilder().AddYaml(yamlPath, level: 0, writeable: true).Build()) { Console.WriteLine($" App:Name = {yamlCfg.Get("App:Name")}"); Console.WriteLine($" Database:Port = {yamlCfg.Get("Database:Port")}"); } Console.WriteLine("\n2.4 TOML 格式:"); using (var tomlCfg = new CfgBuilder().AddToml(tomlPath, level: 0, writeable: true).Build()) { Console.WriteLine($" App:Name = {tomlCfg.Get("App:Name")}"); Console.WriteLine($" Database:Port = {tomlCfg.Get("Database:Port")}"); } // 混合多种格式 Console.WriteLine("\n2.5 混合多种格式(层级覆盖):"); using var mixedCfg = new CfgBuilder() .AddIni(iniPath, level: 0, writeable: false) .AddYaml(yamlPath, level: 1, writeable: false) .AddToml(tomlPath, level: 2, writeable: true, isPrimaryWriter: true) .Build(); Console.WriteLine($" App:Name = {mixedCfg.Get("App:Name")} (来自 TOML,最高优先级)"); Console.WriteLine($" App:Version = {mixedCfg.Get("App:Version")} (来自 TOML)"); File.Delete(iniPath); File.Delete(xmlPath); File.Delete(yamlPath); File.Delete(tomlPath); Console.WriteLine("\n[示例 2 完成]\n"); await Task.CompletedTask; } static async Task Demo3_ConfigSection(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 3: 配置节 (GetSection) 与子键枚举"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); var configPath = Path.Combine(baseDir, "section-demo.json"); File.WriteAllText(configPath, """ { "Database": { "Primary": { "Host": "primary.db.local", "Port": 3306, "Username": "admin" }, "Replica": { "Host": "replica.db.local", "Port": 3307, "Username": "reader" } }, "Cache": { "Redis": { "Host": "redis.local", "Port": 6379 } } } """); using var cfg = new CfgBuilder() .AddJson(configPath, level: 0, writeable: true, isPrimaryWriter: true) .Build(); // 获取配置节 Console.WriteLine("3.1 使用 GetSection 简化嵌套访问:"); var dbSection = cfg.GetSection("Database"); var primarySection = dbSection.GetSection("Primary"); Console.WriteLine($" Database:Primary:Host = {primarySection.Get("Host")}"); Console.WriteLine($" Database:Primary:Port = {primarySection.Get("Port")}"); // 枚举子键 Console.WriteLine("\n3.2 枚举配置节的子键:"); Console.WriteLine(" Database 的子键:"); foreach (var key in dbSection.GetChildKeys()) { Console.WriteLine($" - {key}"); } Console.WriteLine("\n 顶级配置键:"); foreach (var key in cfg.GetChildKeys()) { Console.WriteLine($" - {key}"); } // 通过配置节修改值 Console.WriteLine("\n3.3 通过配置节修改值:"); var replicaSection = dbSection.GetSection("Replica"); replicaSection.Set("Port", "3308"); await cfg.SaveAsync(); Console.WriteLine($" 修改后 Database:Replica:Port = {replicaSection.Get("Port")}"); File.Delete(configPath); Console.WriteLine("\n[示例 3 完成]\n"); } static async Task Demo4_BatchOperations(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 4: 批量操作 - GetMany / SetMany"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); var configPath = Path.Combine(baseDir, "batch-demo.json"); File.WriteAllText(configPath, """ { "Settings": { "Theme": "dark", "Language": "zh-CN", "FontSize": "14", "AutoSave": "true" } } """); using var cfg = new CfgBuilder() .AddJson(configPath, level: 0, writeable: true, isPrimaryWriter: true) .Build(); // 批量获取 Console.WriteLine("4.1 批量获取 (GetMany):"); var keys = new[] { "Settings:Theme", "Settings:Language", "Settings:FontSize" }; var values = cfg.GetMany(keys); foreach (var kv in values) { Console.WriteLine($" {kv.Key} = {kv.Value}"); } // 批量获取并转换类型 Console.WriteLine("\n4.2 批量获取并转换类型 (GetMany):"); var intKeys = new[] { "Settings:FontSize" }; var intValues = cfg.GetMany(intKeys); foreach (var kv in intValues) { Console.WriteLine($" {kv.Key} = {kv.Value} (int)"); } // 批量设置 Console.WriteLine("\n4.3 批量设置 (SetMany):"); var newValues = new Dictionary { ["Settings:Theme"] = "light", ["Settings:FontSize"] = "16", ["Settings:NewOption"] = "enabled" }; cfg.SetMany(newValues); await cfg.SaveAsync(); Console.WriteLine(" 批量设置后的值:"); var updatedValues = cfg.GetMany(new[] { "Settings:Theme", "Settings:FontSize", "Settings:NewOption" }); foreach (var kv in updatedValues) { Console.WriteLine($" {kv.Key} = {kv.Value}"); } File.Delete(configPath); Console.WriteLine("\n[示例 4 完成]\n"); } static async Task Demo5_TypeConversion(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 5: 类型转换"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); var configPath = Path.Combine(baseDir, "types-demo.json"); File.WriteAllText(configPath, """ { "Types": { "IntValue": "42", "LongValue": "9223372036854775807", "DoubleValue": "3.14159", "DecimalValue": "123.456", "BoolTrue": "true", "BoolFalse": "false", "DateValue": "2024-12-25", "GuidValue": "550e8400-e29b-41d4-a716-446655440000", "EnumValue": "Warning" } } """); using var cfg = new CfgBuilder() .AddJson(configPath, level: 0, writeable: false) .Build(); Console.WriteLine("5.1 各种类型转换:"); Console.WriteLine($" int: {cfg.Get("Types:IntValue")}"); Console.WriteLine($" long: {cfg.Get("Types:LongValue")}"); Console.WriteLine($" double: {cfg.Get("Types:DoubleValue")}"); Console.WriteLine($" decimal: {cfg.Get("Types:DecimalValue")}"); Console.WriteLine($" bool (true): {cfg.Get("Types:BoolTrue")}"); Console.WriteLine($" bool (false): {cfg.Get("Types:BoolFalse")}"); Console.WriteLine($" DateTime: {cfg.Get("Types:DateValue"):yyyy-MM-dd}"); Console.WriteLine($" Guid: {cfg.Get("Types:GuidValue")}"); Console.WriteLine($" Enum: {cfg.Get("Types:EnumValue")}"); Console.WriteLine("\n5.2 可空类型与默认值:"); Console.WriteLine($" 不存在的键 (int?): {cfg.Get("Types:NotExist") ?? -1}"); Console.WriteLine($" 不存在的键 (string): {cfg.Get("Types:NotExist") ?? "(null)"}"); File.Delete(configPath); Console.WriteLine("\n[示例 5 完成]\n"); await Task.CompletedTask; } static async Task Demo6_DynamicReload(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 6: 动态配置重载"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); var configPath = Path.Combine(baseDir, "reload-demo.json"); File.WriteAllText(configPath, """ { "App": { "RefreshInterval": "30" } } """); // 启用 reloadOnChange var cfg = new CfgBuilder() .AddJson(configPath, level: 0, writeable: true, isPrimaryWriter: true, reloadOnChange: true) .Build(); Console.WriteLine("6.1 配置动态重载选项:"); var msConfig = cfg.ToMicrosoftConfiguration(new DynamicReloadOptions { DebounceMs = 100, // 防抖时间 EnableDynamicReload = true, // 启用动态重载 Strategy = ReloadStrategy.Eager, // 立即重载 RollbackOnError = true, // 错误时回滚 HistorySize = 5 // 保留 5 条历史 }); Console.WriteLine(" 已配置: DebounceMs=100, Strategy=Eager, HistorySize=5"); // 使用 IChangeToken 监听变更 Console.WriteLine("\n6.2 使用 IChangeToken 监听变更:"); var changeCount = 0; ChangeToken.OnChange( () => msConfig.GetReloadToken(), () => { changeCount++; Console.WriteLine($" [IChangeToken] 配置已更新 (第 {changeCount} 次)"); }); Console.WriteLine(" 已注册 IChangeToken 回调"); // 使用 Rx 订阅配置变更 Console.WriteLine("\n6.3 使用 Rx 订阅配置变更:"); using var subscription = cfg.ConfigChanges.Subscribe(e => { Console.WriteLine($" [Rx] 批次 {e.BatchId} - {e.Changes.Count} 个变更:"); foreach (var (key, change) in e.Changes) { Console.WriteLine($" [{change.Type}] {key}: {change.OldValue} -> {change.NewValue}"); } }); Console.WriteLine(" 已订阅 ConfigChanges"); // 模拟配置变更 Console.WriteLine("\n6.4 模拟配置变更:"); Console.WriteLine(" 修改 App:RefreshInterval 为 60..."); cfg.Set("App:RefreshInterval", "60"); await cfg.SaveAsync(); // 等待变更通知 await Task.Delay(200); Console.WriteLine($"\n 当前值: App:RefreshInterval = {cfg.Get("App:RefreshInterval")}"); cfg.Dispose(); File.Delete(configPath); Console.WriteLine("\n[示例 6 完成]\n"); } static async Task Demo7_DependencyInjection(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 7: 依赖注入集成"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); var configPath = Path.Combine(baseDir, "di-demo.json"); File.WriteAllText(configPath, """ { "Database": { "Host": "db.example.com", "Port": "5432", "Name": "production" }, "Logging": { "Level": "Information", "EnableConsole": "true" } } """); // 配置服务容器 var services = new ServiceCollection(); // 方式1: 使用 AddApqCfg 注册配置 Console.WriteLine("7.1 注册 Apq.Cfg 到 DI 容器:"); services.AddApqCfg(cfg => cfg .AddJson(configPath, level: 0, writeable: true, isPrimaryWriter: true)); Console.WriteLine(" 已注册 ICfgRoot 和 IConfigurationRoot"); // 方式2: 绑定强类型配置 Console.WriteLine("\n7.2 绑定强类型配置:"); services.ConfigureApqCfg("Database"); services.ConfigureApqCfg("Logging"); Console.WriteLine(" 已绑定 DatabaseOptions 和 LoggingOptions"); // 构建服务提供者 var provider = services.BuildServiceProvider(); // 获取服务 Console.WriteLine("\n7.3 从 DI 容器获取服务:"); var cfgRoot = provider.GetRequiredService(); var msConfig = provider.GetRequiredService(); var dbOptions = provider.GetRequiredService>().Value; var logOptions = provider.GetRequiredService>().Value; Console.WriteLine($" ICfgRoot: Database:Host = {cfgRoot.Get("Database:Host")}"); Console.WriteLine($" IConfigurationRoot: Database:Host = {msConfig["Database:Host"]}"); Console.WriteLine($" DatabaseOptions: Host={dbOptions.Host}, Port={dbOptions.Port}, Name={dbOptions.Name}"); Console.WriteLine($" LoggingOptions: Level={logOptions.Level}, EnableConsole={logOptions.EnableConsole}"); // 清理 if (provider is IDisposable disposable) disposable.Dispose(); File.Delete(configPath); Console.WriteLine("\n[示例 7 完成]\n"); await Task.CompletedTask; } static async Task Demo8_EncodingMapping(string baseDir) { Console.WriteLine("═══════════════════════════════════════════════════════════════"); Console.WriteLine("示例 8: 编码映射配置"); Console.WriteLine("═══════════════════════════════════════════════════════════════\n"); var configPath = Path.Combine(baseDir, "encoding-demo.json"); File.WriteAllText(configPath, """ { "App": { "Name": "编码测试应用", "Description": "支持中文和特殊字符: äöü ñ 日本語" } } """, Encoding.UTF8); Console.WriteLine("8.1 编码检测置信度阈值:"); var cfg1 = new CfgBuilder() .WithEncodingConfidenceThreshold(0.7f) .AddJson(configPath, level: 0, writeable: false) .Build(); Console.WriteLine($" 置信度阈值设置为 0.7"); Console.WriteLine($" App:Name = {cfg1.Get("App:Name")}"); cfg1.Dispose(); Console.WriteLine("\n8.2 编码检测日志:"); var cfg2 = new CfgBuilder() .WithEncodingDetectionLogging(result => { Console.WriteLine($" [编码检测] 文件: {Path.GetFileName(result.FilePath)}"); Console.WriteLine($" 编码: {result.Encoding.EncodingName}"); Console.WriteLine($" 置信度: {result.Confidence:P0}"); Console.WriteLine($" 方法: {result.Method}"); }) .AddJson(configPath, level: 0, writeable: false) .Build(); cfg2.Dispose(); Console.WriteLine("\n8.3 编码映射规则:"); Console.WriteLine(" 支持三种映射方式:"); Console.WriteLine(" - 完整路径: AddReadEncodingMapping(path, encoding)"); Console.WriteLine(" - 通配符: AddReadEncodingMappingWildcard(\"*.json\", encoding)"); Console.WriteLine(" - 正则: AddReadEncodingMappingRegex(@\"config.*\\.json$\", encoding)"); // 演示编码映射配置 var cfg3 = new CfgBuilder() // 为特定文件指定编码 .AddReadEncodingMapping(configPath, Encoding.UTF8, priority: 100) // 为所有 JSON 文件指定写入编码 .AddWriteEncodingMappingWildcard("*.json", new UTF8Encoding(false), priority: 50) .AddJson(configPath, level: 0, writeable: false) .Build(); Console.WriteLine("\n 已配置编码映射规则"); Console.WriteLine($" App:Description = {cfg3.Get("App:Description")}"); cfg3.Dispose(); File.Delete(configPath); Console.WriteLine("\n[示例 8 完成]\n"); await Task.CompletedTask; } // ============================================================================ // 强类型配置类 // ============================================================================ public class DatabaseOptions { public string? Host { get; set; } public int Port { get; set; } public string? Name { get; set; } } public class LoggingOptions { public string? Level { get; set; } public bool EnableConsole { get; set; } } // 用于类型转换示例的枚举 public enum LogLevel { Debug, Info, Warning, Error }