1、首先講講ChangeToken.OnChange方法:
原理是給一個CancellationToken注冊一個消費者委托,調用CancellationToken的Cancel的時候會調用這個CancellationToken中所有的委托 代碼實現如下:
public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer) { if (changeTokenProducer == null) { throw new ArgumentNullException("changeTokenProducer"); } if (changeTokenConsumer == null) { throw new ArgumentNullException("changeTokenConsumer"); } Action<object> callback = null; callback = delegate(object s) { IChangeToken changeToken = changeTokenProducer(); try { changeTokenConsumer(); } finally { changeToken.RegisterChangeCallback(callback, null); } }; return changeTokenProducer().RegisterChangeCallback(callback, null); }
2、IOptions<> 生命周期為Singleton,初始化的時候配置就已經存入緩存,並且不再更新
3、IOptionsSnapshot<> 生命周期為Scope,初始化的時候會寫入緩存,內容由OptionsMonitor提供,初始化OptionsMonitor的時候會給所有的IOtionsChangeTokenSource<T>對象的ChangeToken注冊一個重載配置的方法代碼如下
using (IEnumerator<IOptionsChangeTokenSource<TOptions>> enumerator = this._sources.GetEnumerator()) { while (enumerator.MoveNext()) { IOptionsChangeTokenSource<TOptions> source = enumerator.Current; ChangeToken.OnChange(() => source.GetChangeToken(), delegate { this.InvokeChanged(); }); } }
這里的source.GetChangeToken中的Token是從IConfigurationRoot中獲取的,以下代碼可以證明:
public class ConfigurationChangeTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions> { private IConfiguration _config; /// <summary> /// Constructor taking the IConfiguration instance to watch. /// </summary> /// <param name="config">The configuration instance.</param> public ConfigurationChangeTokenSource(IConfiguration config) { if (config == null) { throw new ArgumentNullException("config"); } this._config = config; } /// <summary> /// Returns the reloadToken from IConfiguration. /// </summary> /// <returns></returns> public IChangeToken GetChangeToken() { return this._config.GetReloadToken(); } }
這里的_config就是調用AddOptions.Config(...)方法的時候注冊進去的,而ConfigurationRoot在初始化的時候,會把自己的ChangeToken的Reload事件注冊到所有的IConfigurationProvider對象的ChangeToken,代碼如下
public ConfigurationRoot(IList<IConfigurationProvider> providers) { if (providers == null) { throw new ArgumentNullException("providers"); } this._providers = providers; using (IEnumerator<IConfigurationProvider> enumerator = providers.GetEnumerator()) { while (enumerator.MoveNext()) { IConfigurationProvider p = enumerator.Current; p.Load(); ChangeToken.OnChange(() => p.GetReloadToken(), delegate { this.RaiseChanged(); }); } } } private void RaiseChanged() { //執行自身的_changeToken的OnReload事件並且重新初始化一個ConfigurationReloadToken Interlocked.Exchange<ConfigurationReloadToken>(ref this._changeToken, new ConfigurationReloadToken()).OnReload(); }
這樣就可以保證所有的ConfigurationProvider發生Reload的時候,IConfigurationRoot中的ChangeToken也會發生Reload事件。而我們的配置發生改變的時候,我們的ConfigurationProvider需要先更新Data數據,然后再觸發他的Reload事件,就可以觸發IConfigurationRoot的Reload事件,OptionsMonitor初始化的時候會給IConfigurationRoot的ChangeToken注冊一個更新配置緩存的事件(前面說到過),所以OptionsMonitor就會更新配置緩存,然后下一次請求的時候創建的新IOptionsSnapshot<>接口對象就可以讀取到更新之后的配置信息了