您的位置:首页 > 编程语言 > ASP

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

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


目录

IOptionsMonitor

OptionsMonitor源码探索

ConfigurationChangeTokenSource

示例


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 上看完整的源码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: