ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot
2017-06-28 更新:
OptionsSnapshot?已改為 OptionsManager?變更詳情
IOptionsCache?已改為 IOptionsMonitorCache?變更詳情
在?上一章?中,介紹了?IOptions?的使用, 而我們知道,在 ConfigurationBuilder 的 AddJsonFile 中,有一個 reloadOnChange 參數(shù),設(shè)置為 true 時,在配置文件發(fā)生變化時,會自動更新 IConfigurationRoot ,這是一個非常棒的特性,遺憾的是?IOptions?在配置源發(fā)生變化時,并不會進行更新。好在,微軟還為我們提供了?IOptionsSnapshot?,本章就來探索一下其源碼。
IOptionsSnapshot
IOptionsSnapshot?繼承自IOptions,并擴展了一個Get方法:
public interface IOptionsSnapshot<out TOptions> : IOptions<TOptions> where TOptions : class, new() { ? ?TOptions Get(string name); }看到Get方法的Name參數(shù),我想大家便會想到在?第一章?中所介紹的指定Name的Configure方法,這便是它的用武之地了,通過Name的不同,來配置同一Options類型的多個實例。
那?IOptionsSnapshot?又是如何實現(xiàn)配置的同步的呢?別急,先看一下它與?IOption?的區(qū)別:
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>))); services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsSnapshot<>)));首先很明顯的是:一個是單例,一個指定作用域。其次,IOptionsSnapshot?的實現(xiàn)者是?OptionsSnapshot。
OptionsSnapshot
從名字上來看,便知道它保存的只是一份快照,先看下源碼:
public class OptionsSnapshot<TOptions> : IOptionsSnapshot<TOptions> where TOptions : class, new() { ??private readonly IOptionsFactory<TOptions> _factory; ?
??private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); ?
??
???public OptionsSnapshot(IOptionsFactory<TOptions> factory) ? ?{_factory = factory;} ? ?
???
???public TOptions Value => Get(Options.DefaultName); ?
???
??? ?public virtual TOptions Get(string name) ? ?{ ? ? ?
??? ? ?if (name == null){ ? ? ?
??? ? ?? ? ?throw new ArgumentNullException(nameof(name));} ? ?
??? ? ?// Store the options in our instance cachereturn _cache.GetOrAdd(name, () => _factory.Create(name));} }
代碼很簡單,Options?的創(chuàng)建是通過?IOptionsFactory?來實現(xiàn)的,而?Options?的實例是通過?OptionsCache來保存的。那便去看下他們的源碼。
IOptionsFactory
public interface IOptionsFactory<TOptions> where TOptions : class, new() { ? ?TOptions Create(string name); }
public class OptionsFactory<TOptions> : IOptionsFactory<TOptions> where TOptions : class, new() { ?
?private readonly IEnumerable<IConfigureOptions<TOptions>> _setups; ?
??private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures; ?
??
?? ?public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures) ? ?{_setups = setups;_postConfigures = postConfigures;} ? ?
?? ?
?? ?public TOptions Create(string name) ? ?{ ? ?
?? ? ? ?var options = new TOptions(); ? ?
?? ? ? ?foreach (var setup in _setups){ ? ? ? ?
?? ? ? ?? ?if (setup is IConfigureNamedOptions<TOptions> namedSetup){namedSetup.Configure(name, options);} ? ? ? ?
?? ? ? ?? ??else if (name == Options.DefaultName) ? ?
? ? ? ? ? ?{setup.Configure(options);}} ? ?
?? ? ? ?foreach (var post in _postConfigures){post.PostConfigure(name, options);} ? ? ? ?return options;} }
IOptionsFactory?的默認實現(xiàn)類是?OptionsFactory,在創(chuàng)建Options時,先執(zhí)行所有的Configure方法,然后執(zhí)行PostConfigure方法。在?第一章?中講的PostConfigure方法,也在這里派上用場了。
OptionsCache
OptionsCache?用來緩存?Options?的實例,相當于一個優(yōu)化的Options字典,并使用Lazy實現(xiàn)了延遲初始化,看代碼:
public class OptionsCache<TOptions> : IOptionsCache<TOptions> where TOptions : class{ ??private readonly ConcurrentDictionary<string, Lazy<TOptions>> _cache = new ConcurrentDictionary<string, Lazy<TOptions>>(StringComparer.Ordinal); ? ?
?
?public virtual TOptions GetOrAdd(string name, Func<TOptions> createOptions) ? ?{ ? ? ?
??if (name == null) throw new ArgumentNullException(nameof(name)); ?
??? ? ?if (createOptions == null) throw new ArgumentNullException(nameof(createOptions)); ? ?
??? ? ?? ?return _cache.GetOrAdd(name, new Lazy<TOptions>(createOptions)).Value;} ?
???
????public virtual bool TryAdd(string name, TOptions options) ? ?{ ?
????? ? ?if (name == null)
????? ? ? ? throw new ArgumentNullException(nameof(name)); ? ?
????? ? ?if (options == null) throw new ArgumentNullException(nameof(options)); ? ? ?
????? ? ? ??return _cache.TryAdd(name, new Lazy<TOptions>(() => options));} ?
????
?????public virtual bool TryRemove(string name) ? ?{ ? ?
????? ? ?if (name == null) throw new ArgumentNullException(nameof(name)); ? ? ? ? ? ? ? ??return _cache.TryRemove(name, out var ignored);} }
總結(jié)
IOptionsSnapshot?通過注冊為一個作用域內(nèi)的單例模式,來保證當配置發(fā)生變化時,下一個請求可以獲取到最新的配置。其實在?2.0-preview2?中,OptionsSnapshot?使用了?IOptionsChangeTokenSource?模式,來監(jiān)聽配置的變化,當發(fā)生變化清空?OptionsCache?中的緩存,來實現(xiàn)?Options?的自動更新。當時我還感到困擾:“OptionsSnapshot既然能夠做到配置的更新,怎么還注冊成Scope實例呢?”。而現(xiàn)在缺少了?IOptionsChangeTokenSource?模式的即時更新,或許讓我們感覺不是那么爽,當然也有一個致命的問題,就是當我們在一個自定義的類中使用了?IOptionsSnapshot?,并且這個類本身是以單例的形式注冊的,那么便永遠獲取不到最新的配置了。不過,我們還有最后一個大殺器:?IOptionsMonitor,來滿足我們極致的需求,哈哈,下一篇就來介紹一下它。
相關(guān)文章:?
ASP.NET Core 源碼學習之 Options[1]:Configure
ASP.NET Core 源碼學習之 Options[2]:IOptions
ASP.NET Core MVC 源碼學習:詳解 Action 的匹配
asp.net core源碼飄香:從Hosting開始
asp.net core源碼飄香:Configuration組件
asp.net core源碼飄香:Options組件
asp.net core源碼飄香:Logging組件
原文地址:http://www.cnblogs.com/RainingNight/p/strongly-typed-options-ioptions-in-asp-net-core.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core MVC Tag
- 下一篇: ASP.NET Core 源码学习之 O