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


    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
        {
            /// 
            /// Channel队列
            /// 
            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();
    
            /// 
            /// 使用Channel通知配置变更,如果有事件更新则重新加载
            /// 
            /// 
            /// 
            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();
                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 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();
                return services;
            }
    
            /// 
            /// 提供对IConfiguration,IOptions的支持
            /// 
            /// 
            /// 
            /// 
            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();
                var cache = sp.GetRequiredService();
    
                //使用缓存避免重复反射
                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 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

  • 相关阅读:
    【系统学习】WRF-DA资料同化
    反射&注释API
    C++位图简明介绍与实现
    Java面经丨后端3年经验社招面试题目分享(参考答案)
    【展馆攻略】展馆室内精准定位导航服务,便捷无忧,所见必达!
    Git 使用指南(附详细解释)
    JSP课设:学生选课系统(附源码+调试)
    dubbo分布式日志调用链追踪
    ssl教程易语言代码
    FastJson远程命令执行漏洞学习笔记
  • 原文地址:https://www.cnblogs.com/vipwan/p/18204135