ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot
在 上一章 中,介绍了 IOptions 的使用, 而我们知道,在ConfigurationBuilder的AddJsonFile中,有一个reloadOnChange参数,设置为true时,在配置文件发生变化时,会自动更新IConfigurationRoot,这是一个非常棒的特性,遗憾的是 IOptions 在配置源发生变化时,并不会进行更新。好在,微软还为我们提供了 IOptionsSnapshot ,本篇就来探索一下其源码。
IOptionsSnapshot
IOptionsSnapshot 继承自IOptions,并扩展了一个Get方法:
public interface IOptionsSnapshot<out TOptions> : IOptions<TOptions> where TOptions : class, new()
{
TOptions Get(string name);
}
看到Get方法的Name参数,我想大家便会想到在 第一章 中所介绍的指定Name的Configure方法,这便是它的用武之地了,通过Name的不同,来配置同一Options类型的多个实例。
那 IOptionsSnapshot 又是如何实现配置的同步的呢?别急,先看一下它与 IOption 的区别:
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsSnapshot<>)));
首先很明显的是:一个是单例,一个指定作用域。其次,IOptionsSnapshot 的实现者是 OptionsSnapshot。
OptionsSnapshot
从名字上来看,便知道它保存的只是一份快照,先看下源码:
public class OptionsSnapshot<TOptions> : IOptionsSnapshot<TOptions> where TOptions : class, new()
{
private readonly IOptionsFactory<TOptions> _factory;
private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>();
public OptionsSnapshot(IOptionsFactory<TOptions> factory)
{
_factory = factory;
}
public TOptions Value => Get(Options.DefaultName);
public virtual TOptions Get(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
// Store the options in our instance cache
return _cache.GetOrAdd(name, () => _factory.Create(name));
}
}
代码很简单,Options 的创建是通过 IOptionsFactory 来实现的,而 Options 的实例是通过 OptionsCache来保存的。那便去看下他们的源码。
IOptionsFactory
public interface IOptionsFactory<TOptions> where TOptions : class, new()
{
TOptions Create(string name);
}
public class OptionsFactory<TOptions> : IOptionsFactory<TOptions> where TOptions : class, new()
{
private readonly IEnumerable<IConfigureOptions<TOptions>> _setups;
private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures;
public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures)
{
_setups = setups;
_postConfigures = postConfigures;
}
public TOptions Create(string name)
{
var options = new TOptions();
foreach (var setup in _setups)
{
if (setup is IConfigureNamedOptions<TOptions> namedSetup)
{
namedSetup.Configure(name, options);
}
else if (name == Options.DefaultName)
{
setup.Configure(options);
}
}
foreach (var post in _postConfigures)
{
post.PostConfigure(name, options);
}
return options;
}
}
IOptionsFactory 的默认实现类是 OptionsFactory,在创建Options时,先执行所有的Configure方法,然后执行PostConfigure方法。在 第一章 中讲的PostConfigure方法,也在这里派上用场了。
OptionsCache
OptionsCache 用来缓存 Options 的实例,相当于一个优化的Options字典,并使用Lazy实现了延迟初始化,看代码:
public class OptionsCache<TOptions> : IOptionsCache<TOptions> where TOptions : class
{
private readonly ConcurrentDictionary<string, Lazy<TOptions>> _cache = new ConcurrentDictionary<string, Lazy<TOptions>>(StringComparer.Ordinal);
public virtual TOptions GetOrAdd(string name, Func<TOptions> createOptions)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (createOptions == null) throw new ArgumentNullException(nameof(createOptions));
return _cache.GetOrAdd(name, new Lazy<TOptions>(createOptions)).Value;
}
public virtual bool TryAdd(string name, TOptions options)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (options == null) throw new ArgumentNullException(nameof(options));
return _cache.TryAdd(name, new Lazy<TOptions>(() => options));
}
public virtual bool TryRemove(string name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
return _cache.TryRemove(name, out var ignored);
}
}
总结
IOptionsSnapshot 通过注册为一个作用域内的单例模式,来保证当配置发生变化时,下一个请求可以获取到最新的配置。其实在 2.0-preview2 中,OptionsSnapshot 使用了 IOptionsChangeTokenSource 模式,来监听配置的变化,当发生变化清空 OptionsCache 中的缓存,来实现 Options 的自动更新。当时我还感到困扰:“OptionsSnapshot既然能够做到配置的更新,怎么还注册成Scope实例呢?”。而现在缺少了 IOptionsChangeTokenSource 模式的即时更新,或许让我们感觉不是那么爽,当然也有一个致命的问题,就是当我们在一个自定义的类中使用了 IOptionsSnapshot ,并且这个类本身是以单例的形式注册的,那么便永远获取不到最新的配置了。不过,我们还有最后一个大杀器: IOptionsMonitor,来满足我们极致的需求,哈哈,下一篇就来介绍一下它。
ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot的更多相关文章
- ASP.NET Core 源码学习之 Options[1]:Configure
配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 源码解析 ASP ...
- ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor
前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...
- ASP.NET Core 源码学习之 Options[2]:IOptions
在上一篇中,介绍了一下Options的注册,而使用时只需要注入IOption即可: public ValuesController(IOptions<MyOptions> options) ...
- ASP.NET Core源码学习(一)Hosting
ASP.NET Core源码的学习,我们从Hosting开始, Hosting的GitHub地址为:https://github.com/aspnet/Hosting.git 朋友们可以从以上链接克隆 ...
- ASP.NET Core 源码学习之 Logging[2]:Configure
在上一章中,我们对 ASP.NET Logging 系统做了一个整体的介绍,而在本章中则开始从最基本的配置开始,逐步深入到源码当中去. 默认配置 在 ASP.NET Core 2.0 中,对默认配置做 ...
- ASP.NET Core 源码学习之 Logging[1]:Introduction
在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...
- ASP.NET Core 源码学习之 Logging[3]:Logger
上一章,我们介绍了日志的配置,在熟悉了配置之后,自然是要了解一下在应用程序中如何使用,而本章则从最基本的使用开始,逐步去了解去源码. LoggerFactory 我们可以在构造函数中注入 ILogge ...
- ASP.NET Core 源码学习之 Logging[4]:FileProvider
前面几章介绍了 ASP.NET Core Logging 系统的配置和使用,而对于 Provider ,微软也提供了 Console, Debug, EventSource, TraceSource ...
- 【ASP.NET Core 】ASP.NET Core 源码学习之 Logging[1]:Introduction
在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...
随机推荐
- 使用Spire.Doc组件利用模板导出Word文档
以前一直是用Office的组件实现Word文档导出,但是让客户在服务器安装Office,涉及到版权:而且Office安装,包括权限配置也是比较麻烦. 现在流行使用第三方组件来实现对Office的操作, ...
- 初识Celery
本系列文章的开发环境: window + python2. + pycharm5 + celery3.1.25 + django1.9.4 在我们日常的开发工作中,经常会遇到这几种情况: 1.在web ...
- poj3067 Japan 树状数组求逆序对
题目链接:http://poj.org/problem?id=3067 题目就是让我们求连线后交点的个数 很容易想到将左端点从小到大排序,如果左端点相同则右端点从小到大排序 那么答案即为逆序对的个数 ...
- [刷题]算法竞赛入门经典(第2版) 5-15/UVa12333 - Revenge of Fibonacci
题意:在前100000个Fibonacci(以下简称F)数字里,能否在这100000个F里找出以某些数字作为开头的F.要求找出下标最小的.没找到输出-1. 代码:(Accepted,0.250s) / ...
- TP框架 命名空间 与第三方类
命名空间 相当于虚拟目录 所有类文件都放在虚拟目录 功能:实现自动加载类 TP框架的命名空间要更复杂 内容=> 命名空间中定义和使用 都用\1初始命名空间 相当于 根目录 如:Library文件 ...
- Log4net日志记录、详细配置(自己使用)
[来自百度百科的一句介绍]log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具. 1.首先添 ...
- 使用redis做mybaties的二级缓存(2)-Mybatis 二级缓存小心使用
Mybatis默认对二级缓存是关闭的,一级缓存默认开启: 下面就说说为什么使用二级缓存需要注意: 二级缓存是建立在同一个namespace下的,如果对表的操作查询可能有多个namespace,那么得到 ...
- (入门篇 NettyNIO开发指南)第三章-Netty入门应用
作为Netty的第一个应用程序,我们依然以第2章的时间服务器为例进行开发,通过Netty版本的时间服务报的开发,让初学者尽快学到如何搭建Netty开发环境和!运行Netty应用程序. 如果你已经熟悉N ...
- php原生curl接口的请求
/** * @desc 接口请求处理 * @date 2017/5/19 11:39 * @param [$url请求的接口地址,$way为false为get请求,true为post请求] * @au ...
- R笔记(1):formula和Formula
#####开一个新的系列.关于R的一些笔记,就是遇到过的一些问题的简单整理.可能很基本,也可能没什么大的用处,作为一个记录而已.------------------------------------ ...