上一章 中,介绍了 IOptions 的使用, 而我们知道,在ConfigurationBuilderAddJsonFile中,有一个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 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的更多相关文章

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

    配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 源码解析 ASP ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 【ASP.NET Core 】ASP.NET Core 源码学习之 Logging[1]:Introduction

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

随机推荐

  1. self 和 super 关键字

    self 相当于 java中的this self使用总结 1.self谁调用当前方法,self就代表谁 2.self在对象方法中,self代表当前对象 3.self在类方法中个,self代表类 [se ...

  2. 精选this关键字的指向规律你记住了吗

      1.首先要明确:           谁最终调用函数,this指向谁           this指向的永远只可能是对象!!!!!          this指向谁永远不取决于this写在哪,而取 ...

  3. 深入浅出SOA

    前一阵换了份工作,来到新公司,恰好新同事问起SOA是什么,我随口说了几点,其实自己以前研究过,不过并没有详细的整理过,说的比较模糊,恰好周末,拿出点时间整理下以前对SOA的认知. SOA是什么?SOA ...

  4. 分享一个超级好用的php程序员工具箱

    分享一个超级好用的php程序员工具箱,是由php中文网开发的. 集合了php环境搭建.在线小工具.原生手册.文字与视频教程.问答社区等 (php程序员工具箱 v0.1版本,点此下载:http://ww ...

  5. CAS单点登录服务器搭建

    关于cas单点登录的原理及介绍这里不做说明了,直接开始: 1.war包下载 去官网(https://www.apereo.org/projects/cas/download-cas)下载cas_ser ...

  6. 自学JS

    通过慕课网自学JS,敲了好多代码,好像没什么卵用,附上代码,再接再厉吧! //属性getter setter方法var man = {name : 'wsy', weibo : '@wsy', get ...

  7. JEESZ分布式框架简介

    声明:该框架面向企业,是大型互联网分布式企业架构,后期会介绍Linux上部署高可用集群项目. 项目基础功能截图(自提供了最小部分) 介绍 1.      项目核心代码结构截图 <modules& ...

  8. 基于TypeScript的FineUIMvc组件式开发(开头篇)

    了解FineUIMvc的都知道,FineUIMvc中采用了大量的IFrame框架,对于IFrame的优缺点网上也有很多的讨论,这里我要说它的一个优点“有助于隔离代码逻辑”,这也是FineUIMvc官网 ...

  9. 学习SpringMVC中优秀的代码编写风格

    在org.springframework.web.servlet.FrameworkServlet 中有下面这段代码 private class ContextRefreshListener impl ...

  10. Circuit Breaker Features

    Better to use a circuit breaker which supports the following set of features: Automatically time-out ...