100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 跟我一起学.NetCore之选项(Options)核心类型简介

跟我一起学.NetCore之选项(Options)核心类型简介

时间:2023-02-05 04:04:29

相关推荐

跟我一起学.NetCore之选项(Options)核心类型简介

前言

.NetCore中提供的选项框架,我把其理解为配置组,主要是将服务中可供配置的项提取出来,封装成一个类型;从而服务可根据应用场景进行相关配置项的设置来满足需求,其中使用了依赖注入的形式,使得更加简单、便捷;另外和配置(Configuration)系统的无缝结合,使得服务更加灵活;而对于Options我们经常在注册服务中用到,相信只要接触过.NetCore中的小伙伴都知道,在注册服务的时候,经常在参数中进行Options的配置(如下图),可以直接的说:没有Options的服务不是好服务~~~

正文

Options模型中主要有三个核心接口类型:IOption<TOptions>、IOptionsSnapshot<TOptions>、IOptionsMonitor<TOptions>这里的TOptions就是指针对服务提取出来的配置项封装的类型,以下分别看看三个核心类型定义了什么?

IOption<TOptions>

namespace Microsoft.Extensions.Options{//这里TOptions做了一个约束,必须有无参构造函数public interface IOptions<out TOptions> where TOptions : class, new(){//这里是通过属性的方式定义TOptionsTOptions Value{get;}}}

IOptionsSnapshot<TOptions>

namespace Microsoft.Extensions.Options{// 这里TOptions 做了一个约束,必须有无参构造函数public interface IOptionsSnapshot<out TOptions> : IOptions<TOptions> where TOptions : class, new(){//通过名字获取TOptionsTOptions Get(string name);}}

IOptionsMonitor<TOptions>

namespace Microsoft.Extensions.Options{public interface IOptionsMonitor<out TOptions>{//通过属性获取TOptionsTOptions CurrentValue{get;}//通过名称获取TOptionsTOptions Get(string name);//这是用于监听改变的,如果数据设置项改变,就会发出通知IDisposable OnChange(Action<TOptions, string> listener);}}

通过以上三种类型的定义,大概应该知道TOptions有对应的名字,根据对应的名字创建或获取TOptions,可能会问,IOption中是通过属性获取的,没有指定名字啊,其实是有的,只是名字默认为空,所以称之为默认Option;而对于IOptionsMonitor一看便知,它提供了监听改变的功能,所以后续如果需要监听改变,就可以用这个类型接口;除此,微软为三个核心类型提供了默认实现,IOptions<TOptions>和IOptionsSnapshot<TOptions>的默认实现为OptionsManager<TOptions>,IOptionsMonitor<TOptions>的默认实现为OptionsMonitor<TOptions>,来、走进他们的世界,看看是如何实现的(进行简单的注释):

OptionsManager<TOptions>

//实现了IOptions<TOptions>和IOptionsSnapshot<TOptions>, 同时也约束了TOptionspublic class OptionsManager<TOptions> :IOptions<TOptions>, IOptionsSnapshot<TOptions> where TOptions : class, new(){//用于专门提供TOptions实例的,同时也对TOpions进行相关初始化private readonly IOptionsFactory<TOptions> _factory;//提高性能,将对应的TOptions实例进行缓存private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); // 构造函数,通过依赖注入的形式,将factory进行注入public OptionsManager(IOptionsFactory<TOptions> factory){_factory = factory;}//实现IOptions<TOptions>通过属性获取TOptions实例public TOptions Value{get{// 这里通过一个默认的名字获取,只是这个名字默认为空,所以还是有名字的return Get(Options.DefaultName);}}//实现IOptionsSnapshot<TOptions>通过名字获取TOptionspublic virtual TOptions Get(string name){name = name ?? Options.DefaultName;//如果缓存中没有,就通过传入的Action进行创建并加入到缓存中return _cache.GetOrAdd(name, () => _factory.Create(name));}}//定义的TOptions默认名称public static class Options{public static readonly string DefaultName = string.Empty; }

OptionsMonitor<TOptions>

namespace Microsoft.Extensions.Options{//实现IOptionsMonitor,对TOpitons进行约束public class OptionsMonitor<TOptions> : IOptionsMonitor<TOptions>, IDisposable where TOptions : class, new(){//根据名称缓存TOptions对象private readonly IOptionsMonitorCache<TOptions> _cache;//用于创建TOptions对象private readonly IOptionsFactory<TOptions> _factory;//监听改变的核心private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;private readonly List<IDisposable> _registrations = new List<IDisposable>();//改变触发的事件internal event Action<TOptions, string> _onChange;// 构造函数public OptionsMonitor(IOptionsFactory<TOptions> factory, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources, IOptionsMonitorCache<TOptions> cache){_factory = factory;_sources = sources;_cache = cache;//注册改变回调,用于监听foreach (var source in _sources){var registration = ChangeToken.OnChange(() => source.GetChangeToken(),(name) => InvokeChanged(name),source.Name);_registrations.Add(registration);}}//这个方法是重点,如果发生改变,移除之前的TOptions对象,重新创建一个新TOptionsprivate void InvokeChanged(string name){name = name ?? Options.DefaultName;//根据名字移除TOpitons对象_cache.TryRemove(name);//重新根据名称获取对象var options = Get(name);if (_onChange != null){_onChange.Invoke(options, name);}}//获取默认的TOptions对象public TOptions CurrentValue{get => Get(Options.DefaultName);}//根据名称获取TOptions对象,如果没有就利用OptionsFactory创建TOptions对象public virtual TOptions Get(string name){name = name ?? Options.DefaultName;//return _cache.GetOrAdd(name, () => _factory.Create(name));}// 注册监听改变的蝙蝠public IDisposable OnChange(Action<TOptions, string> listener){var disposable = new ChangeTrackerDisposable(this, listener);_onChange += disposable.OnChange;return disposable;}//取消注册的监听改变回调,同时移除对应的监听Tokenpublic void Dispose(){foreach (var registration in _registrations){registration.Dispose();}_registrations.Clear();}//内部类internal class ChangeTrackerDisposable : IDisposable{private readonly Action<TOptions, string> _listener;private readonly OptionsMonitor<TOptions> _monitor;public ChangeTrackerDisposable(OptionsMonitor<TOptions> monitor, Action<TOptions, string> listener){_listener = listener;_monitor = monitor;}//触发改变时调用对应注册的回调public void OnChange(TOptions options, string name) => _listener.Invoke(options, name);//Dispose方法进行解除注册的监听回调public void Dispose() => _monitor._onChange -= OnChange;}}}

通过以上代码段得知,IOptions<TOptions>和IOptionsSnapshot<TOptions>其实本质都是一样的,都是命名的Options,统一由IOptionsFactory创建提供TOptions对象;而对于IOptionsMonitor<TOptions>,监听的本质是通过IOptionsChangeTokenSource<TOptions>这个实现,每次监听到改变都把对应名称的TOptions对象移除,重新创建一个新的TOptions对象,从而获取到最新的值,其实最终改变通知的本质还是使用IChangeToken进行通知,这个可以参考之前配置的监听改变(参考这篇:跟我一起学.NetCore之配置变更监听);

本想看完以上默认实现,就打算进行举例演示了,不再深入看代码;但是相信看到这的小伙伴肯定会问:IOptionsFactory<TOptions>是怎么创建出TOptions的?重点都不说,差评^_^~~~~~,其实我也是这么想的,所以继续再看看IOptionsFactory<TOptions>;

IOptionsFactory<TOptions>的默认实现是OptionsFactory<TOptions>;创建TOptions我理解为三步骤,创建对象->加工对象(初始化)->验证(验证合法性):

namespace Microsoft.Extensions.Options{public interface IOptionsFactory<TOptions> where TOptions : class, new(){// 接口中定义了一个创建方法,用于创建TOptionsTOptions Create(string name);}}

namespace Microsoft.Extensions.Options{//实现IOptionsFactory接口,并约束TOptionspublic class OptionsFactory<TOptions> : IOptionsFactory<TOptions> where TOptions : class, new(){//初始化逻辑,初始化由IConfigureOptions和IPostConfigureOptions处理private readonly IEnumerable<IConfigureOptions<TOptions>> _setups;private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures;// 验证逻辑private readonly IEnumerable<IValidateOptions<TOptions>> _validations;//构造函数 public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures) : this(setups, postConfigures, validations: null){ }// 构造函数 public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations){_setups = setups;_postConfigures = postConfigures;_validations = validations;}//创建TOptions的核心方法,传入名称,如果没名称,默认是空public TOptions Create(string name){//1根据传入的TOptions创建对象,这里用无参构造函数,所以之前需要对TOptions进行约束var options = new TOptions();// 2 初始化foreach (var setup in _setups){//根据传入的名字是否为默认名称选择不同的加工方法if (setup is IConfigureNamedOptions<TOptions> namedSetup){namedSetup.Configure(name, options);}else if (name == Options.DefaultName){setup.Configure(options);}}//IPostConfigureOptions对Options加工foreach (var post in _postConfigures){post.PostConfigure(name, options);}//进行验证,如果不传入验证规则,则代表不进行验证if (_validations != null){//存放验证失败的错误消息var failures = new List<string>();// 遍历验证foreach (var validate in _validations){// 进行验证var result = validate.Validate(name, options);// 如果验证失败if (result.Failed){ //将验证失败错误信息加入到列表中failures.AddRange(result.Failures);}}//如果验证失败,就抛出异常OptionsValidationExceptionif (failures.Count > 0){throw new OptionsValidationException(name, typeof(TOptions), failures);}}// 返回实例对象return options;}}}

对于TOptions的创建逻辑就暂时先看到这吧,如果需要再详细了解具体逻辑,可以私下进行研究;

总结

哎呀,这篇先不一一举例演示了,可能导致篇幅过长,上个WC的时间估计看不完(哈哈哈);那么就是单纯的代码说明吗?不仅仅如此,这篇主要讲解代码的同时,其实着重凸显了IOption<TOptions>、IOptionsSnapshot<TOptions>、IOptionsMonitor<TOptions>三个核心类型,然后围绕三个核心类型简单看了内部实现、创建过程和监听逻辑,因为在实际应用也是围绕以上进行使用和扩展的,最终的目的是让使用不再糊涂,其实这才是终极目标啦~~~~下一篇就专门针对Options举例演示!!!

----------------------------------------------

一个被程序搞丑的帅小伙,关注"Code综艺圈",识别关注跟我一起学~~~

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。