在 "上一篇" 中,介紹了一下 Options 的註冊,而使用時只需要註入 IOption 即可: IOptions IOptions 定義非常簡單,只有一個 屬性: OptionsManager 而當我們註入 時,其預設實現則是 ,在 擴展方法中可以看到: 而我們在使用的時候,並沒有調用 擴展方法 ...
在 上一篇 中,介紹了一下Options的註冊,而使用時只需要註入 IOption
public ValuesController(IOptions<MyOptions> options)
{
var opt = options.Value;
}
IOptions
IOptions 定義非常簡單,只有一個Value
屬性:
public interface IOptions<out TOptions> where TOptions : class, new()
{
TOptions Value { get; }
}
OptionsManager
而當我們註入IOptions<MyOptions>
時,其預設實現則是OptionsManager<MyOptions>
,在AddOptions
擴展方法中可以看到:
public static IServiceCollection AddOptions(this IServiceCollection services)
{
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
return services;
}
而我們在使用的時候,並沒有調用
AddOptions
擴展方法,那是因為在WebHostBuilder
的BuildCommonServices
方法中進行了調用,具體在Hosting系列中會詳細來說。
再看一下OptionsManager
的源碼:
public class OptionsManager<TOptions> : IOptions<TOptions> where TOptions : class, new()
{
private LegacyOptionsCache<TOptions> _optionsCache;
public OptionsManager(IEnumerable<IConfigureOptions<TOptions>> setups)
{
_optionsCache = new LegacyOptionsCache<TOptions>(setups);
}
public virtual TOptions Value
{
get
{
return _optionsCache.Value;
}
}
}
OptionsManager
的構造函數中註入了IConfigureOptions<T>
,而這裡使用了 IEnumerable 類型,則表示當註冊多個時,則為按順序依次執行。而其IConfigureOptions
則在上一篇已經講過,是通過Configure
擴展方法進行註冊的。而TOptions
的創建工作則是在LegacyOptionsCache
類中:
LegacyOptionsCache
先看代碼,勝過千言萬語:
internal class LegacyOptionsCache<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 LegacyOptionsCache(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);
}
}
}
其實非常簡單,首先使用預設構造函數創建TOptions
實例,然後依次執行IConfigureOptions
的Configure
方法,說明最後一次的配置會覆蓋之前的配置。而且使用了LazyInitializer
來實現雙檢鎖的效果,保證TOptions
只實例化一次。
總結
本文描述了在 .NET Core Options 系統中,IOptions 的使用及實現原理。IOptions 的實現使用的是單例模式,因此當配置源發生變化時,我們無法獲取到最新的配置。如果我們希望能夠檢測到配置源的變化,並能夠自動更新,則可以使用 IOptionsSnapshot 。