欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ASP.NET Core 选项模式源码学习Options IOptions(二)

程序员文章站 2022-05-29 09:22:03
前言 上一篇文章介绍IOptions的注册,本章我们继续往下看 IOptions IOptions是一个接口里面只有一个Values属性,该接口通过OptionsManager实现 OptionsManager OptionsManager实现了IOptions和IOptionsSnapshot,他 ......

前言

上一篇文章介绍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 进行缓存操作;实现ioptionssnapshot接口get(string name)其实就是获取我们第一章所指定的name,通过ioptionsfactory<>创建toptions实例

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