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;
        }
    }