Biwen.Settings添加对IConfiguration&IOptions的集成支持

  • Biwen.Settings添加对IConfiguration&IOptions的集成支持已关闭评论
  • 62 次浏览
  • A+
所属分类:.NET技术
摘要

Biwen.Settings 是一个简易的配置项管理模块,主要的作用就是可以校验并持久化配置项,比如将自己的配置存储到数据库中,JSON文件中等
使用上也是很简单,只需要在服务中注入配置,
比如我们有一个GithubSetting的配置项,我们只需要定义好对象然后注入到Service中即可:

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;//直接对象注入 

尽管这样已经足够好用且便捷,但是对于习惯了使用IConfigurationIOptions的朋友来说还是有些不习惯,其实实现对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