前面我们讲到 IOptionsIOptionsSnapshot,他们两个最大的区别便是前者注册的是单例模式,后者注册的是 Scope 模式。而 IOptionsMonitor 则要求配置源必须是可监听的,用来实现 Options 实例的自动更新,并对外提供了 OnChage 事件,给我们更多的控制权。

目录

  1. IOptionsMonitor
  2. OptionsMonitor源码探索
  3. ConfigurationChangeTokenSource
  4. 示例

IOptionsMonitor

对于 IOptionsMonitor 我们接触的较少,它的定义如下:

public interface IOptionsMonitor<out TOptions>
{
TOptions CurrentValue { get; } TOptions Get(string name); IDisposable OnChange(Action<TOptions, string> listener);
}

AddOptions扩展方法中,可以看到它的默认实现为 OptionsMonitor

public static IServiceCollection AddOptions(this IServiceCollection services)
{
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>)));
services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>)));
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache<>), typeof(OptionsCache<>)));
}

OptionsMonitor源码探索

OptionsMonitor通过IOptionsChangeTokenSource来实现事件的监听,具体如下:

public class OptionsMonitor<TOptions> : IOptionsMonitor<TOptions> where TOptions : class, new()
{
private readonly IOptionsMonitorCache<TOptions> _cache;
private readonly IOptionsFactory<TOptions> _factory;
private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;
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)
{
ChangeToken.OnChange<string>(() => source.GetChangeToken(), (name) => InvokeChanged(name), source.Name);
}
} public TOptions CurrentValue => Get(Options.DefaultName); public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
return _cache.GetOrAdd(name, () => _factory.Create(name));
} public IDisposable OnChange(Action<TOptions, string> listener)
{
...
} private void InvokeChanged(string name)
{
...
}
}

首先看构造函数中的三个参数,其中 IOptionsFactory<>在上一章已讲过,而IOptionsChangeTokenSource则在 第一章 中介绍过,通过IConfiguration进行配置的 Options,会注册该类型的实现,用来实现对配置源的监听:

public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config) where TOptions : class
{
...
services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config));
...
}

IOptionsChangeTokenSource 的定义如下:

public interface IOptionsChangeTokenSource<out TOptions>
{
IChangeToken GetChangeToken(); string Name { get; }
}

OptionsMonitor的构造函数中,通过调用其GetChangeToken方法,获取到 ChangeToken ,在 InvokeChanged 完成 _onChange 事件的调用:

private void InvokeChanged(string name)
{
name = name ?? Options.DefaultName;
_cache.TryRemove(name);
var options = Get(name);
if (_onChange != null)
{
_onChange.Invoke(options, name);
}
}

而对外暴露的OnChange方法,方便我们注册自己的逻辑:

public IDisposable OnChange(Action<TOptions> listener)
{
var disposable = new ChangeTrackerDisposable(this, listener);
_onChange += disposable.OnChange;
return disposable;
}

这里又使用了一个 ChangeTrackerDisposable 的包装类,用来实现事件的注销:

internal class ChangeTrackerDisposable : IDisposable
{
private readonly Action<TOptions> _listener;
private readonly OptionsMonitor<TOptions> _monitor; public ChangeTrackerDisposable(OptionsMonitor<TOptions> monitor, Action<TOptions> listener)
{
_listener = listener;
_monitor = monitor;
} public void OnChange(TOptions options) => _listener.Invoke(options); public void Dispose() => _monitor._onChange -= OnChange;
}

构造函数的最后一个参数IOptionsMonitorCache的默认实现便是 上一章 中介绍的 OptionsCache

ConfigurationChangeTokenSource

IConfigurationChangeTokenSource 的默认实现类是 ConfigurationChangeTokenSource :

public class ConfigurationChangeTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions>
{
private IConfiguration _config; public ConfigurationChangeTokenSource(IConfiguration config) : this(Options.DefaultName, config) { } public ConfigurationChangeTokenSource(string name, IConfiguration config)
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
_config = config;
} public string Name { get; } public IChangeToken GetChangeToken()
{
return _config.GetReloadToken();
}
}

上面用到的 ChangeToken 便是通过构造函数接受的IConfiguration类型的参数来获取的:

public interface IConfiguration
{
... IChangeToken GetReloadToken(); ...
}

因此要想使用IOptionsMonitor,通常要使用IConfiguration来注册才可以,当然,你也可以实现自己的 ConfigurationChangeTokenSource

示例

下面简单演示一下IOptionsMonitor的使用:

首先创建一个控制台程序,添加appsettings.json

{
"Name": "bob"
}

然后修改Program.cs如下:

public class MyOptions
{
public string Name { get; set; }
} class Program
{
private IOptionsMonitor<MyOptions> _options; public Program()
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build(); var serviceCollection = new ServiceCollection(); serviceCollection.Configure<MyOptions>(configuration); var serviceProvider = serviceCollection.BuildServiceProvider();
_options = serviceProvider.GetRequiredService<IOptionsMonitor<MyOptions>>();
} public static void Main(string[] args)
{
new Program().Execute(args);
} public void Execute(string[] args)
{
Console.WriteLine(_options.CurrentValue.Name);
_options.OnChange(_ => Console.WriteLine(_.Name));
Console.ReadKey();
}
}

我们手动修改配置文件,便会触发OnChange事件。

附示例代码地址:https://github.com/RainingNight/AspNetCoreSample/tree/master/src/OptionsSample

总结

本章介绍了 IOptionsMonitor 的实现:通过 IConfiguration 所提供的 ChangeToken ,来注册监听事件,对其 CurrentValue 进行更新。到此,ASP.NET Core 中的 Options 源码也就分析完了,其本身比较简单,并没有太多东西。更具体的可以去 Github 上看完整的源码。

ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor的更多相关文章

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

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

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

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

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

    在 上一章 中,介绍了 IOptions 的使用, 而我们知道,在ConfigurationBuilder的AddJsonFile中,有一个reloadOnChange参数,设置为true时,在配置文 ...

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

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

  5. ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor

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

  6. ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-monitor-in-asp-net-core. ...

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

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

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

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

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

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

随机推荐

  1. [刷题]算法竞赛入门经典 3-12/UVa11809

    书上具体所有题目:http://pan.baidu.com/s/1hssH0KO 题目:算法竞赛入门经典 3-4/UVa11809:Floating-Point Numbers 代码: //UVa11 ...

  2. 第一篇:使用Spark探索经典数据集MovieLens

    前言 MovieLens数据集包含多个用户对多部电影的评级数据,也包括电影元数据信息和用户属性信息. 这个数据集经常用来做推荐系统,机器学习算法的测试数据集.尤其在推荐系统领域,很多著名论文都是基于这 ...

  3. 怎么在vue中使用less

    最近使用vue2.0重构项目, 使用vue-cli脚手架构建, 采用webpack模板, 要在项目中使用less进行样式的编写 首先, 打开终端, 在当前项目目录下安装less npm install ...

  4. R语言机器学习之caret包运用

    在大数据如火如荼的时候,机器学习无疑成为了炙手可热的工具,机器学习是计算机科学和统计学的交叉学科, 旨在通过收集和分析数据的基础上,建立一系列的算法,模型对实际问题进行预测或分类. R语言无疑为我们提 ...

  5. 项目管理之 使用 appledoc 生成开发文档

    写项目时通常会遇到要求写开发文档的需求,但是就源代码来说,文档最好和源码在一起,这样更新起来更加方便和顺手.Objective-C 有一些文档管理工具,doxygen, headdoc 和 apple ...

  6. vue+vux+axios+vuex+vue-router的项目的理解

    本文主要是讲解项目前期的工作,后期考虑再详细说明. 作为一个技术团队如果你们团队选择了上面的技术栈,这说明你们的技术团体对于vue有很熟练的掌握了.在这里我想说明的是前期架构的重要.这里有一遍博客写的 ...

  7. wampServer 2.5 64位 更改"www 目录"不成功

    已经指到自己新目录了,修改了 apache的httpd.conf里面的内容如下 修改: DocumentRoot "e:/phproot/" 修改: <Directory & ...

  8. PHP版本替换, phpinfo和php -v显示版本信息不一致

    环境:OS X EI Capitan 10.11 & lnmp 背景: 1想将lamp(xampp安装的,php5.2)换成 lnmp(php7.0)   2php5.2卸载(xampp卸载& ...

  9. Unity应用架构设计(11)——一个网络层的构建

    对于客户端应用程序,免不了和远程服务打交道.设计一个良好的『服务层』能帮我们规范和分离业务代码,提高生产效率.服务层最核心的模块一定是怎样发送请求,虽然Mono提供了很多C#网络请求类,诸如WebCl ...

  10. SQL万能语句-经典操作

    一.基础 1.说明:创建数据库CREATE DATABASE database-name 2.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备 ...