很久之前写过一篇介绍Options的文章,2016年再打开发现很多变化。增加了新类,增加OptionMonitor相关的类。今天就对于这个现在所谓的新版本进行介绍。
老版本的传送门([Asp.net 5] Options-配置文件之后昂的配置)。
首先上一个图:
*绿线是继承关系,蓝线是关联关系。
我们把上面切成2大部分。
Option部分
这部分分为俩部分,第一部分直接创建Options,该部分通过Options静态类创建一个OptionsWrapper类,之后将(IOptions,OptionsWrapper)进行注入。这部分是DI的实体注入,很简单,没有什么可说的,此处应用非常常见的“工厂模式”。
第二部分是将(IOptions,OptionsManager)进行注入。我们OptionsManager会使用IEnumerable<IConfigureOptions<TOptions>>作为参数,而内部返回的是OptionsCache类型的对象,此处应用非常常见的“代理模式”


internal class OptionsCache<TOptions> where TOptions : class, new(){private readonly Func<TOptions> _createCache;private object _cacheLock = new object();private bool _cacheInitialized;private TOptions _options;private IEnumerable<IConfigureOptions<TOptions>> _setups;public OptionsCache(IEnumerable<IConfigureOptions<TOptions>> setups){_setups = setups;_createCache = CreateOptions;}private TOptions CreateOptions(){var result = new TOptions();if (_setups != null){foreach (var setup in _setups){setup.Configure(result);}}return result;}public virtual TOptions Value{get{return LazyInitializer.EnsureInitialized(ref _options,ref _cacheInitialized,ref _cacheLock,_createCache);}}}
此处附录OptionsCache代码,里面(IConfigureOptions,ConfigureOptions)已经进行注入了。而ConfigureOptions代码如下:


public class ConfigureOptions<TOptions> : IConfigureOptions<TOptions> where TOptions : class{public ConfigureOptions(Action<TOptions> action){if (action == null){throw new ArgumentNullException(nameof(action));}Action = action;}public Action<TOptions> Action { get; private set; }public virtual void Configure(TOptions options){if (options == null){throw new ArgumentNullException(nameof(options));}Action.Invoke(options);}}
而ConfigureOptions实际上只是对Action<TOptions>的封装吧了(这里是不是可以理解为适配器)。
*为什么要传递Action<T>进行配置?我的理解是因为延时性。延时的概念就是,你做的修改不是立马生效,以至于配置的时候,我们都不用考虑先后顺序。
OptionsMonitor部分
OptionsMonitor是对Options的监视器。我决定这部分好像一个调度者模式??。
的IOptionsChangeTokenSource
OptionsMonitor代码如下:


public class OptionsMonitor<TOptions> : IOptionsMonitor<TOptions> where TOptions : class, new(){private OptionsCache<TOptions> _optionsCache;private readonly IEnumerable<IConfigureOptions<TOptions>> _setups;private readonly IEnumerable<IOptionsChangeTokenSource<TOptions>> _sources;public OptionsMonitor(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IOptionsChangeTokenSource<TOptions>> sources){_sources = sources;_setups = setups;_optionsCache = new OptionsCache<TOptions>(setups);}public TOptions CurrentValue{get{return _optionsCache.Value;}}public IDisposable OnChange(Action<TOptions> listener){var disposable = new ChangeTrackerDisposable();foreach (var source in _sources){Action<object> callback = null;IDisposable previousSubscription = null;callback = (s) =>{// The order here is important. We need to take the token and then apply our changes BEFORE// registering. This prevents us from possible having two change updates to process concurrently.//// If the token changes after we take the token, then we'll process the update immediately upon// registering the callback.var token = source.GetChangeToken();// Recompute the options before calling the watchers_optionsCache = new OptionsCache<TOptions>(_setups);listener(_optionsCache.Value);// Remove the old callback after its been firedvar nextSubscription = token.RegisterChangeCallback(callback, s);disposable.Disposables.Add(nextSubscription);disposable.Disposables.Remove(previousSubscription);previousSubscription = nextSubscription;};previousSubscription = source.GetChangeToken().RegisterChangeCallback(callback, state: null);disposable.Disposables.Add(previousSubscription);}return disposable;}}
通过IOptionsChangeTokenSource的IChangeToken对象发出更改请求,之后Action<TOptions> listener进行数据更改。
Onchange方法,实现上就是每次调用都会创建一个新的IDisposable(ChangeTrackerDisposable),如此而已。