ASP.NET Core 選項模式原始碼學習Options IOptions(二)
阿新 • • 發佈:2019-12-13
前言
上一篇文章介紹IOptions的註冊,本章我們繼續往下看
IOptions
IOptions是一個接口裡面只有一個Values屬性,該介面通過OptionsManager實現
public interface IOptions<out TOptions> where TOptions : class, new() { /// <summary> /// The default configured <typeparamref name="TOptions"/> instance /// </summary> TOptions Value { get; } }
OptionsManager
OptionsManager實現了IOptions<>和IOptionsSnapshot<>,他使用內部屬性OptionsCache
public class OptionsManager<TOptions> : IOptions<TOptions>, IOptionsSnapshot<TOptions> where TOptions : class, new() { private readonly IOptionsFactory<TOptions> _factory; private readonly OptionsCache<TOptions> _cache = new OptionsCache<TOptions>(); // Note: this is a private cache /// <summary> /// Initializes a new instance with the specified options configurations. /// </summary> /// <param name="factory">The factory to use to create options.</param> public OptionsManager(IOptionsFactory<TOptions> factory) { _factory = factory; } /// <summary> /// The default configured <typeparamref name="TOptions"/> instance, equivalent to Get(Options.DefaultName). /// </summary> public TOptions Value { get { return Get(Options.DefaultName); } } /// <summary> /// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>. /// </summary> public virtual TOptions Get(string name) { name = name ?? Options.DefaultName; // Store the options in our instance cache return _cache.GetOrAdd(name, () => _factory.Create(name)); } } public interface IOptionsSnapshot<out TOptions> : IOptions<TOptions> where TOptions : class, new() { /// <summary> /// Returns a configured <typeparamref name="TOptions"/> instance with the given name. /// </summary> TOptions Get(string name); }
OptionsCache
OptionsCache採用了執行緒安全字典ConcurrentDictionary進行了封裝用於記憶體快取
public class OptionsCache<TOptions> : IOptionsMonitorCache<TOptions> where TOptions : class { private readonly ConcurrentDictionary<string, Lazy<TOptions>> _cache = new ConcurrentDictionary<string, Lazy<TOptions>>(StringComparer.Ordinal); /// <summary> /// Clears all options instances from the cache. /// </summary> public void Clear() => _cache.Clear(); /// <summary> /// Gets a named options instance, or adds a new instance created with <paramref name="createOptions"/>. /// </summary> /// <param name="name">The name of the options instance.</param> /// <param name="createOptions">The func used to create the new instance.</param> /// <returns>The options instance.</returns> public virtual TOptions GetOrAdd(string name, Func<TOptions> createOptions) { if (createOptions == null) { throw new ArgumentNullException(nameof(createOptions)); } name = name ?? Options.DefaultName; return _cache.GetOrAdd(name, new Lazy<TOptions>(createOptions)).Value; } /// <summary> /// Tries to adds a new option to the cache, will return false if the name already exists. /// </summary> /// <param name="name">The name of the options instance.</param> /// <param name="options">The options instance.</param> /// <returns>Whether anything was added.</returns> public virtual bool TryAdd(string name, TOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } name = name ?? Options.DefaultName; return _cache.TryAdd(name, new Lazy<TOptions>(() => options)); } /// <summary> /// Try to remove an options instance. /// </summary> /// <param name="name">The name of the options instance.</param> /// <returns>Whether anything was removed.</returns> public virtual bool TryRemove(string name) { name = name ?? Options.DefaultName; return _cache.TryRemove(name, out var ignored); } }
OptionsFactory
OptionsFactory實現了 IOptionsFactory.Create(string name);,
而OptionsFactory建構函式中注入了IConfigureOptions<>和IPostConfigureOptions<>,
這裡使用了IEnumerable型別標識當註冊多個時候按照次數依次的執行,從如下程式碼中我們也看到了我們在上一章中所說的Configures和postConfigures註冊先後順序問題。
public class OptionsFactory<TOptions> : IOptionsFactory<TOptions> where TOptions : class, new()
{
private readonly IEnumerable<IConfigureOptions<TOptions>> _setups;
private readonly IEnumerable<IPostConfigureOptions<TOptions>> _postConfigures;
private readonly IEnumerable<IValidateOptions<TOptions>> _validations;
public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures) : this(setups, postConfigures, validations: null)
{ }
public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations)
{
_setups = setups;
_postConfigures = postConfigures;
_validations = validations;
}
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);
}
if (_validations != null)
{
var failures = new List<string>();
foreach (var validate in _validations)
{
var result = validate.Validate(name, options);
if (result.Failed)
{
failures.AddRange(result.Failures);
}
}
if (failures.Count > 0)
{
throw new OptionsValidationException(name, typeof(TOptions), failures);
}
}
return options;
}
}