翼度科技»论坛 编程开发 .net 查看内容

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

8

主题

8

帖子

24

积分

新手上路

Rank: 1

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

来源:https://www.cnblogs.com/vipwan/p/18204135
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具