很久之前寫過一篇介紹Options的文章,2016年再打開發現很多變化。增加了新類,增加OptionMonitor相關的類。今天就對於這個現在所謂的新版本進行介紹。老版本的傳送門([Asp.net 5] Options-配置文件之後昂的配置)。首先上一個圖:*綠線是繼承關係,藍線是關聯關係。我們把上...
很久之前寫過一篇介紹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
此處附錄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
而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 fired var 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; } }OptionsMonitor
通過IOptionsChangeTokenSource的IChangeToken對象發出更改請求,之後Action<TOptions> listener進行數據更改。
Onchange方法,實現上就是每次調用都會創建一個新的IDisposable(ChangeTrackerDisposable),如此而已。