netcore3.0 IOptions 选项(一)

2021-04-03 11:28

阅读:403

标签:服务   技术   exce   默认   tor   vat   amp   enum   sam   

Nuget:以Microsoft.Extensins.Options开头的nuget包

Github地址:https://github.com/dotnet/extensions/tree/master/src/Options

首先看下接口

技术图片

 

 

 

IOptions依赖于服务的依赖注入

首先看下我们需要注册的服务

public static IServiceCollection AddOptions(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions), typeof(OptionsManager)));
            services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot), typeof(OptionsManager)));
            services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor), typeof(OptionsMonitor)));
            services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory), typeof(OptionsFactory)));
            services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitorCache), typeof(OptionsCache)));
            return services;
        }

接下来看下各个接口的具体实现:

一、OptionsManager类:

  

/// 
    /// Implementation of  and .
    /// 
    /// Options type.
    public class OptionsManager : IOptions, IOptionsSnapshotwhere 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));
        }
    }

该类实现IOptions, IOptionsSnapshot接口,当我们注入具体的选项时,依赖注入会解析到对应的OptionsManager实现。

OptionsCache选项的缓存

public class OptionsCache : IOptionsMonitorCachewhere TOptions : class
    {
        private readonly ConcurrentDictionarystring, Lazy> _cache = new ConcurrentDictionarystring, Lazy>(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);
        }
    }

IOptionsFactory接口的实现OptionsFactory

/// 
    /// Implementation of .
    /// 
    /// The type of options being requested.
    public class OptionsFactory : IOptionsFactorywhere TOptions : class, new()
    {
        private readonly IEnumerable> _setups;
        private readonly IEnumerable> _postConfigures;
        private readonly IEnumerable> _validations;

        /// 
        /// Initializes a new instance with the specified options configurations.
        /// 
        /// The configuration actions to run.
        /// The initialization actions to run.
        public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures) : this(setups, postConfigures, validations: null)
        { }

        /// 
        /// Initializes a new instance with the specified options configurations.
        /// 
        /// The configuration actions to run.
        /// The initialization actions to run.
        /// The validations to run.
        public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures, IEnumerable> validations)
        {
            _setups = setups;
            _postConfigures = postConfigures;
            _validations = validations;
        }

        /// 
        /// Returns a configured  instance with the given .
        /// 
        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 Liststring>();
                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;
        }
    }

可以看到OptionsFactory的构造函数接收三个参数:IEnumerable> 、IEnumerable>、IEnumerable>

当依赖注入容器解析OptionsFactory实例时,会找到所有实现IConfigureOptions、IPostConfigureOptions、IValidateOptions的对象

再看下TOptions Create(string name)方法

先遍历IEnumerable>

如果对象实现了IConfigureNamedOptions接口,则调用Configure(name, options)方法,否则判断name == Options.DefaultName是否为true,如果是true,则调用Configure(options)方法

再遍历IEnumerable>

直接调用PostConfigure(name, options)方法

再遍历IEnumerable>方法,主要用于验证结果是否正确,如果验证失败,则抛出异常。

以上是创建IOptions的一个流程

 

再来看下IOptionsMonitor接口,其实现为OptionsMonitor

OptionsMonitor构造函数有三个:IOptionsFactory、IEnumerable>、IOptionsMonitorCache

其中第一和第三个都是系统默认注册好了

第二个参数,如果用户已经注册对应的服务,则遍历该集合,监听选项的变化

 

IOptionsMonitor可以监听选项的变化而重新获取值

 

再来看下一些扩展方法

/// 
        /// Registers an action used to configure a particular type of options.
        /// Note: These are run before all .
        /// 
        /// The options type to be configured.
        /// The  to add the services to.
        /// The action used to configure the options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection Configure(this IServiceCollection services, Action configureOptions) where TOptions : class
            => services.Configure(Options.Options.DefaultName, configureOptions);

        /// 
        /// Registers an action used to configure a particular type of options.
        /// Note: These are run before all .
        /// 
        /// The options type to be configured.
        /// The  to add the services to.
        /// The name of the options instance.
        /// The action used to configure the options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection Configure(this IServiceCollection services, string name, Action configureOptions)
            where TOptions : class
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            if (configureOptions == null)
            {
                throw new ArgumentNullException(nameof(configureOptions));
            }

            services.AddOptions();
            services.AddSingleton>(new ConfigureNamedOptions(name, configureOptions));
            return services;
        }

        /// 
        /// Registers an action used to configure all instances of a particular type of options.
        /// 
        /// The options type to be configured.
        /// The  to add the services to.
        /// The action used to configure the options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection ConfigureAll(this IServiceCollection services, Action configureOptions) where TOptions : class
            => services.Configure(name: null, configureOptions: configureOptions);

        /// 
        /// Registers an action used to initialize a particular type of options.
        /// Note: These are run after all .
        /// 
        /// The options type to be configured.
        /// The  to add the services to.
        /// The action used to configure the options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection PostConfigure(this IServiceCollection services, Action configureOptions) where TOptions : class
            => services.PostConfigure(Options.Options.DefaultName, configureOptions);

        /// 
        /// Registers an action used to configure a particular type of options.
        /// Note: These are run after all .
        /// 
        /// The options type to be configure.
        /// The  to add the services to.
        /// The name of the options instance.
        /// The action used to configure the options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection PostConfigure(this IServiceCollection services, string name, Action configureOptions)
            where TOptions : class
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            if (configureOptions == null)
            {
                throw new ArgumentNullException(nameof(configureOptions));
            }

            services.AddOptions();
            services.AddSingleton>(new PostConfigureOptions(name, configureOptions));
            return services;
        }

        /// 
        /// Registers an action used to post configure all instances of a particular type of options.
        /// Note: These are run after all .
        /// 
        /// The options type to be configured.
        /// The  to add the services to.
        /// The action used to configure the options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection PostConfigureAll(this IServiceCollection services, Action configureOptions) where TOptions : class
            => services.PostConfigure(name: null, configureOptions: configureOptions);

        /// 
        /// Registers a type that will have all of its I[Post]ConfigureOptions registered.
        /// 
        /// The type that will configure options.
        /// The  to add the services to.
        /// The  so that additional calls can be chained.
        public static IServiceCollection ConfigureOptions(this IServiceCollection services) where TConfigureOptions : class
            => services.ConfigureOptions(typeof(TConfigureOptions));

        private static bool IsAction(Type type)
            => (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Action));

        private static IEnumerable FindIConfigureOptions(Type type)
        {
            var serviceTypes = type.GetTypeInfo().ImplementedInterfaces
                .Where(t => t.GetTypeInfo().IsGenericType && 
                (t.GetGenericTypeDefinition() == typeof(IConfigureOptions)
                || t.GetGenericTypeDefinition() == typeof(IPostConfigureOptions)));
            if (!serviceTypes.Any())
            {
                throw new InvalidOperationException(
                    IsAction(type)
                    ? Resources.Error_NoIConfigureOptionsAndAction
                    : Resources.Error_NoIConfigureOptions);
            }
            return serviceTypes;
        }

        /// 
        /// Registers a type that will have all of its I[Post]ConfigureOptions registered.
        /// 
        /// The  to add the services to.
        /// The type that will configure options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection ConfigureOptions(this IServiceCollection services, Type configureType)
        {
            services.AddOptions();
            var serviceTypes = FindIConfigureOptions(configureType);
            foreach (var serviceType in serviceTypes)
            {
                services.AddTransient(serviceType, configureType);
            }
            return services;
        }

        /// 
        /// Registers an object that will have all of its I[Post]ConfigureOptions registered.
        /// 
        /// The  to add the services to.
        /// The instance that will configure options.
        /// The  so that additional calls can be chained.
        public static IServiceCollection ConfigureOptions(this IServiceCollection services, object configureInstance)
        {
            services.AddOptions();
            var serviceTypes = FindIConfigureOptions(configureInstance.GetType());
            foreach (var serviceType in serviceTypes)
            {
                services.AddSingleton(serviceType, configureInstance);
            }
            return services;
        }

        /// 
        /// Gets an options builder that forwards Configure calls for the same  to the underlying service collection.
        /// 
        /// The options type to be configured.
        /// The  to add the services to.
        /// The  so that configure calls can be chained in it.
        public static OptionsBuilder AddOptions(this IServiceCollection services) where TOptions : class
            => services.AddOptions(Options.Options.DefaultName);

        /// 
        /// Gets an options builder that forwards Configure calls for the same named  to the underlying service collection.
        /// 
        /// The options type to be configured.
        /// The  to add the services to.
        /// The name of the options instance.
        /// The  so that configure calls can be chained in it.
        public static OptionsBuilder AddOptions(this IServiceCollection services, string name)
            where TOptions : class
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.AddOptions();
            return new OptionsBuilder(services, name);
        }

 

 

 

netcore3.0 IOptions 选项(一)

标签:服务   技术   exce   默认   tor   vat   amp   enum   sam   

原文地址:https://www.cnblogs.com/lanpingwang/p/12540260.html


评论


亲,登录后才可以留言!