ASP.NET Coreオプションモードソースコード学習Options IOptions(二)
7617 ワード
前言
前の記事ではIOptionsの登録について説明していますが、本章では引き続き下を見てみましょう.
IOptions
Optionsは、OptionsManagerによって実装されるValuesプロパティが1つしかないインタフェースです.
public interface IOptions where TOptions : class, new()
{
///
/// The default configured instance
///
TOptions Value { get; }
}
OptionsManager
OptionsManagerはIOptions<>とIOptionsSnapshot<>を実現し、内部属性OptionsCacheを使用してキャッシュ操作を行う.IOptionsSnapshotインタフェースGet(string name)を実装するには、第1章で指定したNameを取得し、IOptionsFactory<>を使用してTOptionsインスタンスを作成します.
public class OptionsManager : IOptions, IOptionsSnapshot where TOptions : class, new()
{
private readonly IOptionsFactory _factory;
private readonly OptionsCache _cache = new OptionsCache(); // Note: this is a private cache
///
/// Initializes a new instance with the specified options configurations.
///
/// The factory to use to create options.
public OptionsManager(IOptionsFactory factory)
{
_factory = factory;
}
///
/// The default configured instance, equivalent to Get(Options.DefaultName).
///
public TOptions Value
{
get
{
return Get(Options.DefaultName);
}
}
///
/// Returns a configured instance with the given .
///
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 : IOptions where TOptions : class, new()
{
///
/// Returns a configured instance with the given name.
///
TOptions Get(string name);
}
OptionsCache
OptionsCacheは、スレッドセキュリティ辞書ConcurrentDictionaryを使用してメモリキャッシュ用にカプセル化されています.
public class OptionsCache : IOptionsMonitorCache where TOptions : class
{
private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(StringComparer.Ordinal);
///
/// Clears all options instances from the cache.
///
public void Clear() => _cache.Clear();
///
/// Gets a named options instance, or adds a new instance created with .
///
/// The name of the options instance.
/// The func used to create the new instance.
/// The options instance.
public virtual TOptions GetOrAdd(string name, Func createOptions)
{
if (createOptions == null)
{
throw new ArgumentNullException(nameof(createOptions));
}
name = name ?? Options.DefaultName;
return _cache.GetOrAdd(name, new Lazy(createOptions)).Value;
}
///
/// Tries to adds a new option to the cache, will return false if the name already exists.
///
/// The name of the options instance.
/// The options instance.
/// Whether anything was added.
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(() => options));
}
///
/// Try to remove an options instance.
///
/// The name of the options instance.
/// Whether anything was removed.
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タイプIDを使用して複数登録する場合に回数順に実行され、次のコードから前章で述べたConfiguresとpostConfiguresの登録の前後順の問題も見られます.
public class OptionsFactory : IOptionsFactory where TOptions : class, new()
{
private readonly IEnumerable> _setups;
private readonly IEnumerable> _postConfigures;
private readonly IEnumerable> _validations;
public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures) : this(setups, postConfigures, validations: null)
{ }
public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures, IEnumerable> 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 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();
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;
}
}