U:调整Config模块功能调整Server模块集成效果

This commit is contained in:
jimmy committed 2025-11-17 11:28:01 +08:00
1 parent b14721d79e
commit 53d1dd2dbf
17 files changed
+122 -86

No files matched your search

+1
View File
@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using Common; using Common;
using Config; using Config;
using NetClient.DependencyInjection; using NetClient.DependencyInjection;
using Sharp.Infrastructure.Configuration;
using Sharp.Log.Extensions; using Sharp.Log.Extensions;
using Sharp.NetServer.DependencyInjection; using Sharp.NetServer.DependencyInjection;
+1
View File
@@ -19,6 +19,7 @@
<ProjectReference Include="..\NetClient\NetClient.csproj" /> <ProjectReference Include="..\NetClient\NetClient.csproj" />
<ProjectReference Include="..\NetServer\NetServer.csproj" /> <ProjectReference Include="..\NetServer\NetServer.csproj" />
<ProjectReference Include="..\Log\Log.csproj" /> <ProjectReference Include="..\Log\Log.csproj" />
<ProjectReference Include="..\Sharp.Infrastructure\Sharp.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+59 -7
View File
@@ -6,14 +6,16 @@ namespace Config;
/// <summary> /// <summary>
/// 描述默认配置构建时的约定。 /// 描述默认配置构建时的约定。
/// </summary> /// </summary>
public sealed class ConfigDefaultsOptions public sealed class ConfigDefaultsOptions
{
public ConfigDefaultsOptions()
{ {
BasePath = AppContext.BaseDirectory; private readonly IList<ConfigSource> _fluentSources = new List<ConfigSource>();
EnvironmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); public ConfigDefaultsOptions()
{
BasePath = AppContext.BaseDirectory;
EnvironmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")
?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
} }
/// <summary> /// <summary>
@@ -60,6 +62,43 @@ public sealed class ConfigDefaultsOptions
/// 额外的配置源描述,便于手动指定 Optional 或自定义格式。 /// 额外的配置源描述,便于手动指定 Optional 或自定义格式。
/// </summary> /// </summary>
public IList<ConfigSource> AdditionalSources { get; } = new List<ConfigSource>(); public IList<ConfigSource> AdditionalSources { get; } = new List<ConfigSource>();
/// <summary>
/// 通过流式 API 添加 JSON 配置文件(默认 optional)。
/// </summary>
public ConfigDefaultsOptions WithJson(string path, bool optional = true) =>
AddFluentSource(path, ConfigFormat.Json, optional);
/// <summary>
/// 通过流式 API 添加 INI 配置文件(默认 optional)。
/// </summary>
public ConfigDefaultsOptions WithIni(string path, bool optional = true) =>
AddFluentSource(path, ConfigFormat.Ini, optional);
/// <summary>
/// 通过流式 API 添加 XML 配置文件(默认 optional)。
/// </summary>
public ConfigDefaultsOptions WithXml(string path, bool optional = true) =>
AddFluentSource(path, ConfigFormat.Xml, optional);
/// <summary>
/// 通过流式 API 添加 TOML 配置文件(默认 optional)。
/// </summary>
public ConfigDefaultsOptions WithToml(string path, bool optional = true) =>
AddFluentSource(path, ConfigFormat.Toml, optional);
private ConfigDefaultsOptions AddFluentSource(string path, ConfigFormat format, bool optional)
{
_fluentSources.Add(new ConfigSource
{
Path = path,
Format = format,
Optional = optional
});
return this;
}
internal IEnumerable<ConfigSource> GetFluentSources() => _fluentSources;
} }
/// <summary> /// <summary>
@@ -97,6 +136,14 @@ public static class ConfigDefaults
return manager.Get<T>(section, validate); return manager.Get<T>(section, validate);
} }
public static T Load<T>(Action<ConfigDefaultsOptions> defaults,
bool validate = true,
string? section = null,
Action<ConfigOptions>? configure = null) where T : new()
{
return Load<T>(validate, section, defaults, configure);
}
internal static void ApplyDefaults(ConfigOptions target, ConfigDefaultsOptions defaults) internal static void ApplyDefaults(ConfigOptions target, ConfigDefaultsOptions defaults)
{ {
if (defaults.BasePath is not null) if (defaults.BasePath is not null)
@@ -120,6 +167,11 @@ public static class ConfigDefaults
target.Sources.Add(CreateSource(file, optional: true)); target.Sources.Add(CreateSource(file, optional: true));
} }
foreach (var source in defaults.GetFluentSources())
{
target.Sources.Add(source);
}
foreach (var source in defaults.AdditionalSources) foreach (var source in defaults.AdditionalSources)
{ {
target.Sources.Add(source); target.Sources.Add(source);
@@ -1,45 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Config;
public interface ISystemConfigAccessor
{
SystemConfig Value { get; }
}
internal sealed class SystemConfigAccessor : ISystemConfigAccessor, IDisposable
{
private readonly ConfigManager _cm;
private readonly IDisposable _subscription;
private SystemConfig _current;
public SystemConfigAccessor(ConfigManager cm)
{
_cm = cm;
_current = cm.Get<SystemConfig>(section: "System", validate: false);
_subscription = cm.OnReload(() =>
{
// Reload on change
_current = _cm.Get<SystemConfig>(section: "System", validate: false);
});
}
public SystemConfig Value => _current;
public void Dispose() => _subscription.Dispose();
}
public static class SystemConfigExtensions
{
/// <summary>
/// Registers SystemConfig accessor backed by ConfigManager under the "System" section.
/// </summary>
public static IServiceCollection AddSystemConfig(this IServiceCollection services, ConfigManager cm)
{
services.TryAddSingleton(cm);
services.TryAddSingleton<ISystemConfigAccessor, SystemConfigAccessor>();
return services;
}
}
+2 -1
View File
@@ -46,7 +46,8 @@ var settings = ConfigDefaults.Load<AppSettings>();
var manager = ConfigDefaults.BuildDefault(opt => var manager = ConfigDefaults.BuildDefault(opt =>
{ {
opt.EnvironmentPrefix = "APP"; // 仅读取 APP__ 前缀的环境变量 opt.EnvironmentPrefix = "APP"; // 仅读取 APP__ 前缀的环境变量
opt.AdditionalFiles.Add("extra.ini"); // 按需附加其它文件,默认 optional opt.WithIni("extra.ini") // 同时加载 INI
.WithToml("feature.toml"); // 以及 TOML
}); });
var settings = manager.Get<AppSettings>(validate: true); var settings = manager.Get<AppSettings>(validate: true);
``` ```
-22
View File
@@ -1,22 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Config;
/// <summary>
/// Global system-level configuration, shared across modules.
/// </summary>
public sealed class SystemConfig
{
[Required]
[RegularExpression(@"^([0-9]{1,3}\.){3}[0-9]{1,3}$|^localhost$|^[a-zA-Z0-9_.-]+$")]
public string HTTPIp { get; set; } = "127.0.0.1";
[Range(1, 65535)]
public int HttpPort { get; set; } = 8080;
[Range(1, 600)]
public int TimeoutSeconds { get; set; } = 30;
public string Scheme { get; set; } = "http";
}
+2 -1
View File
@@ -11,7 +11,8 @@ var settings = ConfigDefaults.Load<AppSettings>();
// 如果需要继续访问 IConfiguration,可先拿到 ConfigManager // 如果需要继续访问 IConfiguration,可先拿到 ConfigManager
var cm = ConfigDefaults.BuildDefault(defaults => var cm = ConfigDefaults.BuildDefault(defaults =>
{ {
defaults.AdditionalFiles.Add("extra.ini"); defaults.WithIni("extra.ini")
.WithToml("feature.toml");
defaults.EnvironmentPrefix = "APP"; defaults.EnvironmentPrefix = "APP";
}); });
var settings2 = cm.Get<AppSettings>(validate: true); var settings2 = cm.Get<AppSettings>(validate: true);
@@ -2,6 +2,7 @@ using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Config; using Config;
using NetClient.Options; using NetClient.Options;
using Sharp.Infrastructure.Configuration;
namespace NetClient.DependencyInjection; namespace NetClient.DependencyInjection;
@@ -44,4 +45,3 @@ public static class ConfigurationExtensions
return services; return services;
} }
} }
+1
View File
@@ -16,6 +16,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" /> <ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\Config\Config.csproj" /> <ProjectReference Include="..\Config\Config.csproj" />
<ProjectReference Include="..\Sharp.Infrastructure\Sharp.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -1,6 +1,7 @@
using Config; using Config;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Sharp.Infrastructure.Configuration;
using Sharp.NetServer.Server; using Sharp.NetServer.Server;
namespace Sharp.NetServer.DependencyInjection; namespace Sharp.NetServer.DependencyInjection;
@@ -26,4 +27,3 @@ public static class ServiceCollectionExtensions
return services; return services;
} }
} }
+1
View File
@@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" /> <ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\Config\Config.csproj" /> <ProjectReference Include="..\Config\Config.csproj" />
<ProjectReference Include="..\Sharp.Infrastructure\Sharp.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -6,4 +6,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Config\Config.csproj" />
</ItemGroup>
</Project> </Project>
@@ -1,10 +1,9 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 此代码由工具生成。 // This code was generated by a tool.
// 运行时版本:4.0.30319.42000
// //
// 对此文件的更改可能会导致不正确的行为,并且如果 // Changes to this file may cause incorrect behavior and will be lost if
// 重新生成代码,这些更改将会丢失。 // the code is regenerated.
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -14,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Sharp.Wpf")] [assembly: System.Reflection.AssemblyCompanyAttribute("Sharp.Wpf")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+989747a2ae8392dd304ec136a20c73d2714a43b9")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b14721d79ee49603fd8600872fa22c984017bb5b")]
[assembly: System.Reflection.AssemblyProductAttribute("Sharp.Wpf")] [assembly: System.Reflection.AssemblyProductAttribute("Sharp.Wpf")]
[assembly: System.Reflection.AssemblyTitleAttribute("Sharp.Wpf")] [assembly: System.Reflection.AssemblyTitleAttribute("Sharp.Wpf")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
@@ -1 +1 @@
6c5c7e08ff66373e9981bab4e0ba90b429207193706551d9719bf8bb6534127e 9709e2b151cbd6395e6164fc9a4c93fdc4813fdcdf520d95d8fe511f945f3f99
+1 -1
View File
@@ -1 +1 @@
17633435623876895 17633497485167484
+1
View File
@@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="MSTest" Version="3.6.4"/> <PackageReference Include="MSTest" Version="3.6.4"/>
<PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+42 -1
View File
@@ -115,6 +115,44 @@ namespace Config.Test
Assert.AreEqual("fromToml", settings.RequiredField); 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] [TestMethod]
public void Validation_Fails_On_Missing_Required() public void Validation_Fails_On_Missing_Required()
{ {
@@ -245,7 +283,10 @@ namespace Config.Test
_ = cm.LoadFromFile<AppSettings>(toml, ConfigFormat.Toml); _ = cm.LoadFromFile<AppSettings>(toml, ConfigFormat.Toml);
}); });
var cmToml = ConfigBuilder.BuildFromFiles(new[] { "a.toml" }, basePath: scope.Dir); var cmToml = ConfigBuilder.BuildFromFiles(
new[] { "a.toml" },
basePath: scope.Dir,
includeEnvironment: false);
var tomlBack = cmToml.Get<AppSettings>(); var tomlBack = cmToml.Get<AppSettings>();
Assert.AreEqual("save", tomlBack.Name); Assert.AreEqual("save", tomlBack.Name);
Assert.AreEqual("db", tomlBack.Database.Host); Assert.AreEqual("db", tomlBack.Database.Host);