U:调整所有代码格式
This commit is contained in:
1 parent
d133bacf55
commit
155b67e831
342 files changed
+9431
-9868
No files matched your search
+302
-300
@@ -7,319 +7,321 @@ using System.Text;
|
||||
// - 使用 MSTest。请确保已在测试项目中添加对 src\Sharp\Config\Config.csproj 的 ProjectReference。
|
||||
// - 约定:配置项的“后添加覆盖先添加”(Configuration 的默认行为)。环境变量位于最末,优先级最高。
|
||||
|
||||
namespace Config.Test
|
||||
namespace Config.Test;
|
||||
|
||||
// 为避免文件监听与并发写入导致的互相影响,禁用并行执行
|
||||
[TestClass]
|
||||
[DoNotParallelize]
|
||||
public class ConfigTests
|
||||
{
|
||||
// 为避免文件监听与并发写入导致的互相影响,禁用并行执行
|
||||
[TestClass]
|
||||
[DoNotParallelize]
|
||||
public class ConfigTests
|
||||
// 简单的配置模型,涵盖:基础字段、嵌套对象、DataAnnotations 验证
|
||||
public class AppSettings
|
||||
{
|
||||
// 简单的配置模型,涵盖:基础字段、嵌套对象、DataAnnotations 验证
|
||||
public class AppSettings
|
||||
public string? Name { get; set; }
|
||||
public int Port { get; set; }
|
||||
|
||||
[Range(1, 65535)] public int ValidPort { get; set; }
|
||||
|
||||
[Required] public string? RequiredField { get; set; }
|
||||
|
||||
public DatabaseSettings Database { get; set; } = new();
|
||||
}
|
||||
|
||||
public class DatabaseSettings
|
||||
{
|
||||
[Required] public string? Host { get; set; }
|
||||
|
||||
public string? User { get; set; }
|
||||
public string? Password { get; set; }
|
||||
}
|
||||
|
||||
// 创建临时目录的轻量封装,便于测试隔离
|
||||
private sealed class TempDirScope : IDisposable
|
||||
{
|
||||
public string Dir { get; }
|
||||
|
||||
public TempDirScope()
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public int Port { get; set; }
|
||||
|
||||
[Range(1, 65535)] public int ValidPort { get; set; }
|
||||
|
||||
[Required] public string? RequiredField { get; set; }
|
||||
|
||||
public DatabaseSettings Database { get; set; } = new();
|
||||
Dir = Path.Combine(Path.GetTempPath(), "ConfigTests_" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(Dir);
|
||||
}
|
||||
|
||||
public class DatabaseSettings
|
||||
public string PathOf(string file)
|
||||
{
|
||||
[Required] public string? Host { get; set; }
|
||||
|
||||
public string? User { get; set; }
|
||||
public string? Password { get; set; }
|
||||
return Path.Combine(Dir, file);
|
||||
}
|
||||
|
||||
// 创建临时目录的轻量封装,便于测试隔离
|
||||
private sealed class TempDirScope : IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
public string Dir { get; }
|
||||
|
||||
public TempDirScope()
|
||||
{
|
||||
Dir = Path.Combine(Path.GetTempPath(), "ConfigTests_" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(Dir);
|
||||
}
|
||||
|
||||
public string PathOf(string file) => System.IO.Path.Combine(Dir, file);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(Dir, recursive: true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* 文件监视可能暂占用,忽略 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Build_FromFiles_Binds_Json_Ini_Xml_Toml_WithValidation()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
// 1. 构造多格式配置文件,分别写入不同字段
|
||||
var jsonPath = scope.PathOf("appsettings.json");
|
||||
var iniPath = scope.PathOf("settings.ini");
|
||||
var xmlPath = scope.PathOf("extra.xml");
|
||||
var tomlPath = scope.PathOf("feature.toml");
|
||||
|
||||
File.WriteAllText(jsonPath, /* UTF-8 无 BOM */
|
||||
"{\n" +
|
||||
" \"Name\": \"fromJson\",\n" +
|
||||
" \"Port\": 10\n" +
|
||||
"}", new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(iniPath,
|
||||
// INI 文件:根键与一层嵌套(Database 节)
|
||||
"User=iniUser\n" +
|
||||
"[Database]\n" +
|
||||
"Host=iniHost\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(xmlPath,
|
||||
// XML:顶层键直接映射
|
||||
"<settings>\n" +
|
||||
" <ValidPort>100</ValidPort>\n" +
|
||||
"</settings>", new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(tomlPath,
|
||||
"RequiredField = \"fromToml\"\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
// 2. 构建配置(顺序:JSON -> INI -> XML -> TOML),最后添加环境变量
|
||||
var cm = ConfigBuilder.BuildFromFiles(
|
||||
files: new[] { "appsettings.json", "settings.ini", "extra.xml", "feature.toml" },
|
||||
reloadOnChange: true,
|
||||
includeEnvironment: true,
|
||||
environmentPrefix: "APP_",
|
||||
basePath: scope.Dir
|
||||
);
|
||||
|
||||
// 3. 强类型绑定 + 验证(DataAnnotations)
|
||||
var settings = cm.Get<AppSettings>(validate: true);
|
||||
|
||||
Assert.AreEqual("fromJson", settings.Name);
|
||||
Assert.AreEqual(10, settings.Port);
|
||||
Assert.AreEqual(100, settings.ValidPort);
|
||||
Assert.AreEqual("iniHost", settings.Database.Host);
|
||||
Assert.AreEqual("iniUser", settings.Database.User);
|
||||
Assert.AreEqual("fromToml", settings.RequiredField);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConfigDefaults_Fluent_WithToml_AndOtherFormats_Works()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"default\", \"Port\": 20 }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(scope.PathOf("settings.ini"),
|
||||
"[Database]\nHost=iniHost\nUser=iniUser\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(scope.PathOf("extra.xml"),
|
||||
"<settings><ValidPort>8080</ValidPort></settings>",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(scope.PathOf("feature.toml"),
|
||||
"RequiredField = \"fromToml\"\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
var settings = ConfigDefaults.Load<AppSettings>(defaults =>
|
||||
{
|
||||
defaults.BasePath = scope.Dir;
|
||||
defaults.IncludeEnvironmentVariables = false;
|
||||
defaults.WithIni("settings.ini")
|
||||
.WithXml("extra.xml")
|
||||
.WithToml("feature.toml");
|
||||
});
|
||||
|
||||
Assert.AreEqual("default", settings.Name);
|
||||
Assert.AreEqual(20, settings.Port);
|
||||
Assert.AreEqual(8080, settings.ValidPort);
|
||||
Assert.AreEqual("iniHost", settings.Database.Host);
|
||||
Assert.AreEqual("iniUser", settings.Database.User);
|
||||
Assert.AreEqual("fromToml", settings.RequiredField);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Validation_Fails_On_Missing_Required()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
// 缺少 RequiredField 与 Database.Host,期望验证失败
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"x\", \"Port\": 5, \"ValidPort\": 200 }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
var cm = ConfigBuilder.BuildFromFiles(new[] { "appsettings.json" }, basePath: scope.Dir);
|
||||
|
||||
Assert.ThrowsException<ConfigurationValidationException>(() => { cm.Get<AppSettings>(validate: true); });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnvironmentVariables_Override_FileValues()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"fromFile\", \"Database\": { \"Host\": \"fileHost\" } }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
// 使用前缀 APP_,配置键用双下划线分隔层级
|
||||
Environment.SetEnvironmentVariable("APP__Name", "fromEnv");
|
||||
Environment.SetEnvironmentVariable("APP__Database__Host", "envHost");
|
||||
|
||||
try
|
||||
{
|
||||
var cm = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "appsettings.json" },
|
||||
basePath: scope.Dir,
|
||||
includeEnvironment: true,
|
||||
environmentPrefix: "APP_"
|
||||
);
|
||||
|
||||
var settings = cm.Get<AppSettings>();
|
||||
Assert.AreEqual("fromEnv", settings.Name);
|
||||
Assert.AreEqual("envHost", settings.Database.Host);
|
||||
Directory.Delete(Dir, true);
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
// 清理环境变量,避免影响其他测试
|
||||
Environment.SetEnvironmentVariable("APP__Name", null);
|
||||
Environment.SetEnvironmentVariable("APP__Database__Host", null);
|
||||
/* 文件监视可能暂占用,忽略 */
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Timeout(15000)]
|
||||
public async Task HotReload_FileChange_Triggers_Callback_And_Rebind()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
var json = scope.PathOf("hot.json");
|
||||
File.WriteAllText(json, "{ \"Port\": 1 }", new UTF8Encoding(false));
|
||||
|
||||
var cm = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "hot.json" },
|
||||
reloadOnChange: true,
|
||||
basePath: scope.Dir
|
||||
);
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
using var sub = cm.OnReload(() => tcs.TrySetResult(true));
|
||||
|
||||
// 初始读取
|
||||
var s1 = cm.Get<AppSettings>();
|
||||
Assert.AreEqual(1, s1.Port);
|
||||
|
||||
// 修改内容,触发文件监控
|
||||
await Task.Delay(250); // 稍等,确保监控挂上
|
||||
File.WriteAllText(json, "{ \"Port\": 2 }", new UTF8Encoding(false));
|
||||
|
||||
// 等待回调触发
|
||||
var fired = await Task.WhenAny(tcs.Task, Task.Delay(5000)) == tcs.Task;
|
||||
Assert.IsTrue(fired, "未在期望时间内触发热更新回调");
|
||||
|
||||
// 变更后再取配置
|
||||
var s2 = cm.Get<AppSettings>();
|
||||
Assert.AreEqual(2, s2.Port);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Save_And_Load_Json_Xml_Ini_Toml()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
var settings = new AppSettings
|
||||
{
|
||||
Name = "save",
|
||||
Port = 1234,
|
||||
ValidPort = 80,
|
||||
RequiredField = "req",
|
||||
Database = new DatabaseSettings { Host = "db", User = "u" }
|
||||
};
|
||||
|
||||
var cm = ConfigManager.Build(new ConfigOptions()); // 用于调用 Save/Load
|
||||
|
||||
// JSON
|
||||
var json = scope.PathOf("a.json");
|
||||
cm.Save(settings, json, ConfigFormat.Json);
|
||||
var jsonBack = cm.LoadFromFile<AppSettings>(json, ConfigFormat.Json);
|
||||
Assert.AreEqual("save", jsonBack!.Name);
|
||||
Assert.AreEqual(1234, jsonBack.Port);
|
||||
Assert.AreEqual("db", jsonBack.Database.Host);
|
||||
|
||||
// XML
|
||||
var xml = scope.PathOf("a.xml");
|
||||
cm.Save(settings, xml, ConfigFormat.Xml);
|
||||
var xmlBack = cm.LoadFromFile<AppSettings>(xml, ConfigFormat.Xml);
|
||||
Assert.AreEqual(80, xmlBack!.ValidPort);
|
||||
|
||||
// INI(支持简单与一层嵌套的序列化回读)
|
||||
var ini = scope.PathOf("a.ini");
|
||||
cm.Save(settings, ini, ConfigFormat.Ini);
|
||||
var iniBack = cm.LoadFromFile<AppSettings>(ini, ConfigFormat.Ini);
|
||||
Assert.AreEqual("u", iniBack!.Database.User);
|
||||
Assert.AreEqual("db", iniBack.Database.Host);
|
||||
|
||||
// TOML:当前不支持直接泛型反序列化(LoadFromFile),需通过配置管线加载
|
||||
var toml = scope.PathOf("a.toml");
|
||||
cm.Save(settings, toml, ConfigFormat.Toml);
|
||||
|
||||
Assert.ThrowsException<NotSupportedException>(() =>
|
||||
{
|
||||
_ = cm.LoadFromFile<AppSettings>(toml, ConfigFormat.Toml);
|
||||
});
|
||||
|
||||
var cmToml = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "a.toml" },
|
||||
basePath: scope.Dir,
|
||||
includeEnvironment: false);
|
||||
var tomlBack = cmToml.Get<AppSettings>();
|
||||
Assert.AreEqual("save", tomlBack.Name);
|
||||
Assert.AreEqual("db", tomlBack.Database.Host);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConfigureBuilderHook_Appends_Providers_And_Overrides()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"fileName\" }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
var cm = ConfigBuilder.Build(opt =>
|
||||
{
|
||||
opt.BasePath = scope.Dir;
|
||||
opt.Sources.Add(new ConfigSource
|
||||
{
|
||||
Path = "appsettings.json", Format = ConfigFormat.Json, Optional
|
||||
= false
|
||||
});
|
||||
// 通过 Hook 追加内存配置,位于最后,覆盖前面的值
|
||||
opt.ConfigureBuilderHook = builder =>
|
||||
{
|
||||
var kv = new[] { new KeyValuePair<string, string?>("Name", "hookName") };
|
||||
builder.AddInMemoryCollection(kv);
|
||||
return builder;
|
||||
};
|
||||
});
|
||||
|
||||
var s = cm.Get<AppSettings>();
|
||||
Assert.AreEqual("hookName", s.Name, "Hook 未按预期覆盖前序配置源");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Build_FromFiles_Binds_Json_Ini_Xml_Toml_WithValidation()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
// 1. 构造多格式配置文件,分别写入不同字段
|
||||
var jsonPath = scope.PathOf("appsettings.json");
|
||||
var iniPath = scope.PathOf("settings.ini");
|
||||
var xmlPath = scope.PathOf("extra.xml");
|
||||
var tomlPath = scope.PathOf("feature.toml");
|
||||
|
||||
File.WriteAllText(jsonPath, /* UTF-8 无 BOM */
|
||||
"{\n" +
|
||||
" \"Name\": \"fromJson\",\n" +
|
||||
" \"Port\": 10\n" +
|
||||
"}", new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(iniPath,
|
||||
// INI 文件:根键与一层嵌套(Database 节)
|
||||
"User=iniUser\n" +
|
||||
"[Database]\n" +
|
||||
"Host=iniHost\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(xmlPath,
|
||||
// XML:顶层键直接映射
|
||||
"<settings>\n" +
|
||||
" <ValidPort>100</ValidPort>\n" +
|
||||
"</settings>", new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(tomlPath,
|
||||
"RequiredField = \"fromToml\"\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
// 2. 构建配置(顺序:JSON -> INI -> XML -> TOML),最后添加环境变量
|
||||
var cm = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "appsettings.json", "settings.ini", "extra.xml", "feature.toml" },
|
||||
true,
|
||||
true,
|
||||
"APP_",
|
||||
scope.Dir
|
||||
);
|
||||
|
||||
// 3. 强类型绑定 + 验证(DataAnnotations)
|
||||
var settings = cm.Get<AppSettings>(validate: true);
|
||||
|
||||
Assert.AreEqual("fromJson", settings.Name);
|
||||
Assert.AreEqual(10, settings.Port);
|
||||
Assert.AreEqual(100, settings.ValidPort);
|
||||
Assert.AreEqual("iniHost", settings.Database.Host);
|
||||
Assert.AreEqual("iniUser", settings.Database.User);
|
||||
Assert.AreEqual("fromToml", settings.RequiredField);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConfigDefaults_Fluent_WithToml_AndOtherFormats_Works()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"default\", \"Port\": 20 }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(scope.PathOf("settings.ini"),
|
||||
"[Database]\nHost=iniHost\nUser=iniUser\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(scope.PathOf("extra.xml"),
|
||||
"<settings><ValidPort>8080</ValidPort></settings>",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
File.WriteAllText(scope.PathOf("feature.toml"),
|
||||
"RequiredField = \"fromToml\"\n",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
var settings = ConfigDefaults.Load<AppSettings>(defaults =>
|
||||
{
|
||||
defaults.BasePath = scope.Dir;
|
||||
defaults.IncludeEnvironmentVariables = false;
|
||||
defaults.WithIni("settings.ini")
|
||||
.WithXml("extra.xml")
|
||||
.WithToml("feature.toml");
|
||||
});
|
||||
|
||||
Assert.AreEqual("default", settings.Name);
|
||||
Assert.AreEqual(20, settings.Port);
|
||||
Assert.AreEqual(8080, settings.ValidPort);
|
||||
Assert.AreEqual("iniHost", settings.Database.Host);
|
||||
Assert.AreEqual("iniUser", settings.Database.User);
|
||||
Assert.AreEqual("fromToml", settings.RequiredField);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Validation_Fails_On_Missing_Required()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
// 缺少 RequiredField 与 Database.Host,期望验证失败
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"x\", \"Port\": 5, \"ValidPort\": 200 }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
var cm = ConfigBuilder.BuildFromFiles(new[] { "appsettings.json" }, basePath: scope.Dir);
|
||||
|
||||
Assert.ThrowsException<ConfigurationValidationException>(() => { cm.Get<AppSettings>(validate: true); });
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EnvironmentVariables_Override_FileValues()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"fromFile\", \"Database\": { \"Host\": \"fileHost\" } }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
// 使用前缀 APP_,配置键用双下划线分隔层级
|
||||
Environment.SetEnvironmentVariable("APP__Name", "fromEnv");
|
||||
Environment.SetEnvironmentVariable("APP__Database__Host", "envHost");
|
||||
|
||||
try
|
||||
{
|
||||
var cm = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "appsettings.json" },
|
||||
basePath: scope.Dir,
|
||||
includeEnvironment: true,
|
||||
environmentPrefix: "APP_"
|
||||
);
|
||||
|
||||
var settings = cm.Get<AppSettings>();
|
||||
Assert.AreEqual("fromEnv", settings.Name);
|
||||
Assert.AreEqual("envHost", settings.Database.Host);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 清理环境变量,避免影响其他测试
|
||||
Environment.SetEnvironmentVariable("APP__Name", null);
|
||||
Environment.SetEnvironmentVariable("APP__Database__Host", null);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[Timeout(15000)]
|
||||
public async Task HotReload_FileChange_Triggers_Callback_And_Rebind()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
var json = scope.PathOf("hot.json");
|
||||
File.WriteAllText(json, "{ \"Port\": 1 }", new UTF8Encoding(false));
|
||||
|
||||
var cm = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "hot.json" },
|
||||
true,
|
||||
basePath: scope.Dir
|
||||
);
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
using var sub = cm.OnReload(() => tcs.TrySetResult(true));
|
||||
|
||||
// 初始读取
|
||||
var s1 = cm.Get<AppSettings>();
|
||||
Assert.AreEqual(1, s1.Port);
|
||||
|
||||
// 修改内容,触发文件监控
|
||||
await Task.Delay(250); // 稍等,确保监控挂上
|
||||
File.WriteAllText(json, "{ \"Port\": 2 }", new UTF8Encoding(false));
|
||||
|
||||
// 等待回调触发
|
||||
var fired = await Task.WhenAny(tcs.Task, Task.Delay(5000)) == tcs.Task;
|
||||
Assert.IsTrue(fired, "未在期望时间内触发热更新回调");
|
||||
|
||||
// 变更后再取配置
|
||||
var s2 = cm.Get<AppSettings>();
|
||||
Assert.AreEqual(2, s2.Port);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Save_And_Load_Json_Xml_Ini_Toml()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
var settings = new AppSettings
|
||||
{
|
||||
Name = "save",
|
||||
Port = 1234,
|
||||
ValidPort = 80,
|
||||
RequiredField = "req",
|
||||
Database = new DatabaseSettings { Host = "db", User = "u" }
|
||||
};
|
||||
|
||||
var cm = ConfigManager.Build(new ConfigOptions()); // 用于调用 Save/Load
|
||||
|
||||
// JSON
|
||||
var json = scope.PathOf("a.json");
|
||||
cm.Save(settings, json, ConfigFormat.Json);
|
||||
var jsonBack = cm.LoadFromFile<AppSettings>(json, ConfigFormat.Json);
|
||||
Assert.AreEqual("save", jsonBack!.Name);
|
||||
Assert.AreEqual(1234, jsonBack.Port);
|
||||
Assert.AreEqual("db", jsonBack.Database.Host);
|
||||
|
||||
// XML
|
||||
var xml = scope.PathOf("a.xml");
|
||||
cm.Save(settings, xml, ConfigFormat.Xml);
|
||||
var xmlBack = cm.LoadFromFile<AppSettings>(xml, ConfigFormat.Xml);
|
||||
Assert.AreEqual(80, xmlBack!.ValidPort);
|
||||
|
||||
// INI(支持简单与一层嵌套的序列化回读)
|
||||
var ini = scope.PathOf("a.ini");
|
||||
cm.Save(settings, ini, ConfigFormat.Ini);
|
||||
var iniBack = cm.LoadFromFile<AppSettings>(ini, ConfigFormat.Ini);
|
||||
Assert.AreEqual("u", iniBack!.Database.User);
|
||||
Assert.AreEqual("db", iniBack.Database.Host);
|
||||
|
||||
// TOML:当前不支持直接泛型反序列化(LoadFromFile),需通过配置管线加载
|
||||
var toml = scope.PathOf("a.toml");
|
||||
cm.Save(settings, toml, ConfigFormat.Toml);
|
||||
|
||||
Assert.ThrowsException<NotSupportedException>(() =>
|
||||
{
|
||||
_ = cm.LoadFromFile<AppSettings>(toml, ConfigFormat.Toml);
|
||||
});
|
||||
|
||||
var cmToml = ConfigBuilder.BuildFromFiles(
|
||||
new[] { "a.toml" },
|
||||
basePath: scope.Dir,
|
||||
includeEnvironment: false);
|
||||
var tomlBack = cmToml.Get<AppSettings>();
|
||||
Assert.AreEqual("save", tomlBack.Name);
|
||||
Assert.AreEqual("db", tomlBack.Database.Host);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ConfigureBuilderHook_Appends_Providers_And_Overrides()
|
||||
{
|
||||
using var scope = new TempDirScope();
|
||||
|
||||
File.WriteAllText(scope.PathOf("appsettings.json"),
|
||||
"{ \"Name\": \"fileName\" }",
|
||||
new UTF8Encoding(false));
|
||||
|
||||
var cm = ConfigBuilder.Build(opt =>
|
||||
{
|
||||
opt.BasePath = scope.Dir;
|
||||
opt.Sources.Add(new ConfigSource
|
||||
{
|
||||
Path = "appsettings.json", Format = ConfigFormat.Json, Optional
|
||||
= false
|
||||
});
|
||||
// 通过 Hook 追加内存配置,位于最后,覆盖前面的值
|
||||
opt.ConfigureBuilderHook = builder =>
|
||||
{
|
||||
var kv = new[] { new KeyValuePair<string, string?>("Name", "hookName") };
|
||||
builder.AddInMemoryCollection(kv);
|
||||
return builder;
|
||||
};
|
||||
});
|
||||
|
||||
var s = cm.Get<AppSettings>();
|
||||
Assert.AreEqual("hookName", s.Name, "Hook 未按预期覆盖前序配置源");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user