2017-06-28 更新:
OptionsSnapshot 已改为 OptionsManager 变更详情
IOptionsCache 已改为 IOptionsMonitorCache 变更详情

在 上一章 中,介绍了 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参数,我想大家便会想到在 第一章 中所介绍的指定NameConfigure方法,这便是它的用武之地了,通过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 cachereturn _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[1]:Configure

  • ASP.NET Core 源码学习之 Options[2]:IOptions

  • ASP.NET Core MVC 源码学习:详解 Action 的匹配

  • asp.net core源码飘香:从Hosting开始

  • asp.net core源码飘香:Configuration组件

  • asp.net core源码飘香:Options组件

  • asp.net core源码飘香:Logging组件

原文地址:http://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-in-asp-net-core.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot相关推荐

  1. ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor

    前面我们讲到 IOptions 和 IOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式.而 IOptionsMonitor 则要求配置源必须是 ...

  2. ASP.NET Core 源码学习之 Options[2]:IOptions

    在 上一篇 中,介绍了一下Options的注册,而使用时只需要注入 IOption 即可: public ValuesController(IOptions<MyOptions> opti ...

  3. ASP.NET Core 源码学习之 Options[1]:Configure

    ASP.NET Core 配置系统 在ASP.NET 4.X中,通常将配置存储在 web.config 中,使用静态帮助类来获取这些配置,而对 web.cofng 中进行任何修改时,则会导致应用程序池 ...

  4. ASP.NET Core 源码学习之 Logging[2]:Configure

    在上一章中,我们对 ASP.NET Logging 系统做了一个整体的介绍,而在本章中则开始从最基本的配置开始,逐步深入到源码当中去. 默认配置 在 ASP.NET Core 2.0 中,对默认配置做 ...

  5. ASP.NET Core 源码学习之Logging[1]:Introduction

    在ASP.NET 4.X中,我们通常使用 log4net, NLog 等来记录日志,但是当我们引用的一些第三方类库使用不同的日志框架时,就比较混乱了.而在 ASP.Net Core 中内置了日志系统, ...

  6. asp.net core源码飘香:Options组件

    简介: Options组件是一个小组件,但用的地方很多.它本质是将一个POCO类注册到容器中(主要在Startup中作为其他组件的配置功能提供),后续使用的时候就可以通过比如构造函数注入等获取到POC ...

  7. ASP.NET Core源码学习(一)Hosting

    ASP.NET Core源码的学习,我们从Hosting开始, Hosting的GitHub地址为:https://github.com/aspnet/Hosting.git 朋友们可以从以上链接克隆 ...

  8. ASP.NET Core 源码学习之 Logging[3]:Logger

    上一章,我们介绍了日志的配置,在熟悉了配置之后,自然是要了解一下在应用程序中如何使用,而本章则从最基本的使用开始,逐步去了解去源码. LoggerFactory 我们可以在构造函数中注入 ILogge ...

  9. ASP.NET Core 源码学习之 Logging[4]:FileProvider

    前面几章介绍了 ASP.NET Core Logging 系统的配置和使用,而对于 Provider ,微软也提供了 Console, Debug, EventSource, TraceSource ...

最新文章

  1. 开源一年,阿里轻量级AI推理引擎MNN 1.0.0正式发布
  2. windows核心编程之进程间共享数据
  3. 头像裁剪上传_微信新功能:11月微信新玩法!给微信换上“皇冠头像”,还能加姓氏?...
  4. C# SQLite编程总结
  5. Bootstrap初学(一)
  6. string插入字符_String类
  7. 大咖面对面 | 陈果果博士谈智能语音
  8. 运用时间增益方法对 gprmax 模拟的探地雷达数据进行处理
  9. 《Arduino开发实战指南:机器人卷》一3.6 编程原理与示例程序
  10. Luogu1919 【模板】A*B Problem升级版(FFT快速傅里叶)
  11. 微信小程序 轮播图展示,图片全屏显示
  12. 五路循迹传感器的优点_用五路循迹传感器做循迹
  13. 【毕业设计】指纹识别系统设计与实现 - 单片机 嵌入式 物联网
  14. Android Studio首次连接不上网易mumu模拟器解决办法
  15. 京东到家订单派发的技术实战
  16. 【请求第三方 url 异常】Illegal character in scheme name at index x (已解决)
  17. 北邮复试 | 北邮机试往年题汇总 | 计算机院 | 网研院 | 网安院 | 软院
  18. 中国版Meerkat来了,趣播手中有一利器,可大幅拉低直播流量
  19. 【html+js+jquery简单表单验证和删查】
  20. WebDAV之葫芦儿·派盘+NMM

热门文章

  1. 免费都不管用,Win10升级在中国怎么就这么难?
  2. lsattr/chattr
  3. 窥探Swift编程之强大的Switch
  4. mysql-普通查询(General Query)慢查询(Slow Query)相关日志配置
  5. js 操作cookies 方法
  6. 如何获取当前C#程序所有线程的调用栈信息 ?
  7. 如何将 CSV 导入到 SqlServer 中 ?
  8. ABP vNext微服务架构详细教程——结束语
  9. 体验.NET Core使用IKVM对接Java
  10. C# WPF MVVM模式Prism框架下事件发布与订阅