跟我一起学.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(){// 这里是通过属性的方式定义TOptions    TOptions 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>{// 通过属性获取TOptions    TOptions 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>, 同时也约束了TOptions
public 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>通过名字获取TOptions public 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综艺圈",识别关注跟我一起学~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307666.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《ASP.NET Core项目开发实战入门》送书活动结果公布

截至2020.09.20 本次送书活动《ASP.NET Core项目开发实战入门》。下面把Top 5的留言截图给大家回顾一下。以下5位同学将获赠书籍一本&#xff1a;小林子鉴静红脸先生阿星Plus以上同学请在2020年9月25日24&#xff1a;00之前加小二微信领取赠书&#xff0c;超过时间视为放弃。小…

二分查找基础概念与经典题目(Leetcode题解-Python语言)二分索引型

二分查找的定义如下&#xff08;引自Wiki&#xff09;&#xff1a; 在计算机科学中&#xff0c;二分查找算法&#xff08;英语&#xff1a;binary search algorithm&#xff09;&#xff0c;也称折半搜索算法&#xff08;英语&#xff1a;half-interval search algorithm&…

Magicodes.IE 2.3重磅发布——.NET Core开源导入导出库

在2.3这一版本的更新中&#xff0c;我们迎来了众多的使用者、贡献者&#xff0c;在这个里程碑中我们也添加并修复了一些功能。对于新特点的功能我将在下面进行详细的描述&#xff0c;当然也欢迎更多的人可以加入进来&#xff0c;再或者也很期待大家来提issues或者PR&#xff0c…

听说用 C# 写 TensorFlow 更高效?

经过半年呕心沥血的努力&#xff0c;SciSharp STACK终于把Tensorflow .NET绑定升级到可以使用 tensorflow 2.3, 新版本最大的优势是实现了Eager模式, 这个特性是让.NET C#/ F#成为机器学习模型开发工具的重要前置条件。NugGet包下载:https://www.nuget.org/packages/TensorFlow…

leetcode279. 完全平方数

一:题目 二:上码 class Solution { public:/**思路:1.分析题意这个就是将一个数分成几个数的和;然而的话,这几个数必须的是完全平方数,我们要求的是最少数量的完全平方数这个满足答案的有好几个;但是我们要求的是最少的数量2.动态规划五步走1>:确定dp数组的含义以及下标的含…

跟我一起学.NetCore之Options实例演示及分析

前言来啦&#xff01;来啦&#xff01;上一节一堆代码&#xff0c;是不是感觉甚是无味啊&#xff1f;没关系&#xff0c;这里结合上一节内容专注举例演示&#xff0c;绝不废话&#xff01;走起~~~~~正文老规矩&#xff0c;一个WebApi项目走起&#xff0c;项目结构如下&#xff…

leetcode139. 单词拆分

一:题目 二:上码 class Solution { public:/**思路:1.分析题意单词就是物品;字符串就是背包;单词能否组成字符串就是在问,物品能不能将背包装满单词可以重复使用那么说明这是一个完全背包2.动态规划五步走1>:确定dp数组的与下标的含义&#xff08;这里用下标i是由我们的遍历…

大数据下的质量体系建设

一、背景大数据、人工智能是当前也是未来几年IT部门的重点建设方向&#xff0c;新的技术可以为业务突破盈利瓶颈&#xff0c;带来新的增长点&#xff0c;同时我们也发现数据中台也频频在最近的企业财报予以体现&#xff0c;相关的技术岗位需求也是供不应求&#xff0c;与之形成…

Pandas中的 transform() 结合 groupby() 用法示例

首先&#xff0c;假设我们有如下餐厅数据集&#xff1a; import pandas as pddf pd.DataFrame({restaurant_id: [101,102,103,104,105,106,107],address: [A,B,C,D, E, F, G],city: [London,London,London,Oxford,Oxford, Durham, Durham],sales: [10,500,48,12,21,22,14] })…

跟我一起学.NetCore之日志(Log)模型核心

前言鲁迅都说&#xff1a;没有日志的系统不能上线(鲁迅说&#xff1a;这句我没说过&#xff0c;但是在理)&#xff01;日志对于一个系统而言&#xff0c;特别重要&#xff0c;不管是用于事务审计&#xff0c;还是用于系统排错&#xff0c;还是用于安全追踪.....都扮演了很重要的…

Numpy中数组创建函数的辨析

首先推荐Numpy官方的教程&#xff0c;网址。 很多人会对数组创建函数的参数中什么时候要用括号np.zeros((2, 3))&#xff0c;什么时候不用括号np.eye(3, 5)感到疑惑&#xff0c;这里对它们统一进行梳理。&#xff08;按照官方文档的分类方法&#xff09; 1. 一维数组创建函数…

leetcode213. 打家劫舍 II

一:题目 二:上码 class Solution { public:/**思路:1.既然成环了,我们如果选取得一条偷取路径是从头开始得那么我们就不能偷取最后一个,那就不算最后一个偷取一遍2.同理我们也可以不算第一个 偷取一遍计算一次偷取得结果*/int rob(vector<int>& nums) {if(nums.size…

一文弄懂Numpy中ndarray的维度(dimension)/轴数(axis/axes)问题

Numpy库的核心是ndarray&#xff0c;实际上就是N维数组&#xff08;N-dimensional array&#xff09;&#xff0c;关于这个数据对象的详细介绍&#xff0c;参考官方文档最为合适。有一点要注意的是&#xff0c;ndarray的内置方法只有30多个&#xff0c;常用的如求平均值可以写a…

leetcode337. 打家劫舍 III

一:题目 二:上码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*…

asp.net core 从 3.1 到 5.0

asp.net core 从 3.1 到 5.0Intro就在前几天&#xff0c;微软宣布了 .NET5 发布了 RC1 版本&#xff0c;这也意味着 .NET5 的开发基本稳定了&#xff0c;正式发布之前&#xff0c;不会再新增新的 Feature&#xff0c;只会专注于修复 BUG 提高稳定性。对于开发者来说&#xff0c…

leetcoed123. 买卖股票的最佳时机 III

一&#xff1a;题目 二:上码 class Solution { public:/**思路:1.动态规划五步走1>:确定dp数组以及下标的含义因为题目给出至多完成两笔交易 那么我们一天的状态就有5种0 无操作1 第一次买入2 第一次卖出3 第二次买入4 第二次卖出dp[i][j] 表示的是在第i天 [0,4] 其中某个…

送福利 | 送书3本 ASP.NET Core 真机拆解

小编&#xff1a;最近.NET相关图书在多年沉寂后重新恢复&#xff0c;本书作者提供3本送给公众号粉丝&#xff0c;所以参与方式&#xff1a;文章下方留言&#xff0c;你可以聊聊.NET Core 这几年的发展给你的印象&#xff0c;你的感想&#xff0c;点赞最多的前5位获奖。活动截止…

异方差 的 BP检验 方法及原理详解

异方差 的 BP检验详解            文章目录 1. `BP`检验的步骤2. 场景示例步骤 ①步骤 ②BP检验,也称为Breusch-Pagan检验,是一种用于检验线性回归模型中异方差性(即误差项方差不恒定)的统计方法。该方法由Trevor S. Breusch和Adrian R. Pagan在1980年提出。 1.…

leetcode309. 最佳买卖股票时机含冷冻期

一&#xff1a;题目 二:上码 class Solution { public:/**思路:1.分析题意那么我们会有四种状态0 买入股票(或者说是之前就买入了股票但是也一直没有操作)1 卖出股票的状态一 两天前就卖出了股票 但是一直没有操作 2 卖出股票状态二 今天卖出股票3 冷冻期 持续一天2.动态规…

.NET Core 下使用 Exceptionless 记录日志

ExceptionLess是一套免费开源分布式系统日志收集框架&#xff0c;也是我无意中发现的&#xff0c;支持自己部署和平台托管的方式接入使用。ExceptionLess官网&#xff1a;https://exceptionless.comExceptionLess开源地址&#xff1a;https://github.com/exceptionless/Excepti…