- A+
所属分类:.NET技术
Biwen.Settings 是一个简易的配置项管理模块,主要的作用就是可以校验并持久化配置项,比如将自己的配置存储到数据库中,JSON文件中等
使用上也是很简单,只需要在服务中注入配置,
比如我们有一个GithubSetting的配置项,我们只需要定义好对象然后注入到Service中即可:
[Description("Github配置")] public class GithubSetting : ValidationSettingBase<GithubSetting> { [Description("Github用户名")] public string? UserName { get; set; } = "vipwan"; [Description("Github仓库")] public string? Repository { get; set; } = "Biwen.Settings"; [Description("Github Token")] public string? Token { get; set; } = ""; public GithubSetting() { //验证规则 RuleFor(x => x.UserName).NotEmpty().Length(3, 128); RuleFor(x => x.Repository).NotNull().NotEmpty().Length(3, 128); RuleFor(x => x.Token).NotNull().NotEmpty().Length(3, 128); } }
@inject GithubSetting GithubSetting;//直接对象注入
尽管这样已经足够好用且便捷,但是对于习惯了使用IConfiguration
和IOptions
的朋友来说还是有些不习惯,其实实现对IConfiguration的支持还是很简单的,实现一下IConfigurationProvider
即可,我们来动手实现一个名为BiwenSettingConfigurationProvider
的Provider:
internal class Events { /// <summary> /// Channel队列 /// </summary> public static readonly Channel<(bool IsChanged, string? SettingName)> ConfigrationChangedChannel = Channel.CreateUnbounded<(bool IsChanged, string? SettingName)>(); } internal sealed class BiwenSettingConfigurationSource(bool autoRefresh = true) : IConfigurationSource { public IConfigurationProvider Build(IConfigurationBuilder builder) => new BiwenSettingConfigurationProvider(autoRefresh); } internal class BiwenSettingConfigurationProvider : ConfigurationProvider, IDisposable, IAsyncDisposable { public BiwenSettingConfigurationProvider(bool autoRefresh) { if (Settings.ServiceRegistration.ServiceProvider is null) { throw new BiwenException("必须首先注册Biwen.Setting模块,请调用:services.AddBiwenSettings()"); } if (autoRefresh) { StartAlertAsync(cts.Token); } } private CancellationTokenSource cts = new(); /// <summary> /// 使用Channel通知配置变更,如果有事件更新则重新加载 /// </summary> /// <param name="cancellationToken"></param> /// <returns></returns> public Task StartAlertAsync(CancellationToken cancellationToken) { _ = Task.Run(async () => { while (!cancellationToken.IsCancellationRequested) { _ = await Events.ConfigrationChangedChannel.Reader.ReadAsync(cancellationToken); Load(); //通知配置变更 OnReload(); } }, cancellationToken); return Task.CompletedTask; } //从SettingManager中加载配置项 public override void Load() { Dictionary<string, string?> dics = []; using var scope = Settings.ServiceRegistration.ServiceProvider.CreateScope(); var settingManager = scope.ServiceProvider.GetRequiredService<ISettingManager>(); var settings = settingManager.GetAllSettings()!; foreach (var setting in settings) { if (setting.SettingContent is null) continue; if (JsonNode.Parse(setting.SettingContent) is not JsonObject json) continue; foreach (var item in json) { dics.TryAdd($"{setting.SettingName}:{item.Key}", item.Value?.ToString()); } } Data = dics; } public void Dispose() { cts.Cancel(); Events.ConfigrationChangedChannel.Writer.Complete(); } public ValueTask DisposeAsync() { cts.Cancel(); Events.ConfigrationChangedChannel.Writer.Complete(); return ValueTask.CompletedTask; } }
内部通过Channel实现变更通知,
internal class ConfigurationMediratorDoneHandler(ILogger<ConfigurationMediratorDoneHandler> logger) : IMediratorDoneHandler { public Task OnPublishedAsync<T>(T @event) where T : ISetting, new() { Events.ConfigrationChangedChannel.Writer.TryWrite((true, typeof(T).Name)); logger.LogInformation($"Setting Changed: {typeof(T).Name},并通知Configuration刷新!"); return Task.CompletedTask; } }
然后老规矩我们扩展一下IServiceCollection
:
public static class ServiceRegistration { internal static IServiceCollection AddBiwenSettingConfiguration(this IServiceCollection services) { //ConfigurationMediratorDoneHandler services.AddSingleton<IMediratorDoneHandler, ConfigurationMediratorDoneHandler>(); return services; } /// <summary> /// 提供对IConfiguration,IOptions的支持 /// </summary> /// <param name="manager"></param> /// <param name="autoRefresh"></param> /// <returns></returns> public static ConfigurationManager AddBiwenSettingConfiguration( this ConfigurationManager manager, IServiceCollection serviceDescriptors, bool autoRefresh = true) { var sp = Settings.ServiceRegistration.ServiceProvider ?? throw new BiwenException("必须首先注册Biwen.Setting模块,请调用:services.AddBiwenSettings()"); //添加订阅 if (autoRefresh) { serviceDescriptors.AddBiwenSettingConfiguration(); } IConfigurationBuilder configBuilder = manager; configBuilder.Add(new BiwenSettingConfigurationSource(autoRefresh)); var settings = ASS.InAllRequiredAssemblies.ThatInherit(typeof(ISetting)).Where(x => x.IsClass && !x.IsAbstract).ToList(); //注册ISetting settings.ForEach(x => { //IOptions DI manager?.GetSection(x.Name).Bind(GetSetting(x, sp)); }); return manager; } static object GetSetting(Type x, IServiceProvider sp) { var settingManager = sp.GetRequiredService<ISettingManager>(); var cache = sp.GetRequiredService<IMemoryCache>(); //使用缓存避免重复反射 var md = cache.GetOrCreate($"GenericMethod_{x.FullName}", entry => { MethodInfo methodLoad = settingManager.GetType().GetMethod(nameof(settingManager.Get))!; MethodInfo generic = methodLoad.MakeGenericMethod(x); return generic; }); return md!.Invoke(settingManager, null)!; } }
最后在启动时调用AddBiwenSettingConfiguration扩展即可
builder.Configuration.AddBiwenSettingConfiguration(builder.Services, true);
最后按下面的形式注册就可以了:
@inject GithubSetting GithubSetting;//直接对象注入 @inject IOptions<GithubSetting> IOP; //通过IOptions注入 @inject IConfiguration Configuration;//IConfiguration ...
源代码我发布到了GitHub,欢迎star! https://github.com/vipwan/Biwen.Settings
https://github.com/vipwan/Biwen.Settings/tree/master/Biwen.Settings/Extensions/Configuration