zoukankan      html  css  js  c++  java
  • netcore3.0 IOptions 选项(一)

    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类:

      

    /// <summary>
        /// Implementation of <see cref="IOptions{TOptions}"/> and <see cref="IOptionsSnapshot{TOptions}"/>.
        /// </summary>
        /// <typeparam name="TOptions">Options type.</typeparam>
        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));
            }
        }

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

    OptionsCache选项的缓存

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

    IOptionsFactory<TOptions>接口的实现OptionsFactory<TOptions>

    /// <summary>
        /// Implementation of <see cref="IOptionsFactory{TOptions}"/>.
        /// </summary>
        /// <typeparam name="TOptions">The type of options being requested.</typeparam>
        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;
    
            /// <summary>
            /// Initializes a new instance with the specified options configurations.
            /// </summary>
            /// <param name="setups">The configuration actions to run.</param>
            /// <param name="postConfigures">The initialization actions to run.</param>
            public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures) : this(setups, postConfigures, validations: null)
            { }
    
            /// <summary>
            /// Initializes a new instance with the specified options configurations.
            /// </summary>
            /// <param name="setups">The configuration actions to run.</param>
            /// <param name="postConfigures">The initialization actions to run.</param>
            /// <param name="validations">The validations to run.</param>
            public OptionsFactory(IEnumerable<IConfigureOptions<TOptions>> setups, IEnumerable<IPostConfigureOptions<TOptions>> postConfigures, IEnumerable<IValidateOptions<TOptions>> validations)
            {
                _setups = setups;
                _postConfigures = postConfigures;
                _validations = validations;
            }
    
            /// <summary>
            /// Returns a configured <typeparamref name="TOptions"/> instance with the given <paramref name="name"/>.
            /// </summary>
            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;
            }
        }

    可以看到OptionsFactory的构造函数接收三个参数:IEnumerable<IConfigureOptions<TOptions>> 、IEnumerable<IPostConfigureOptions<TOptions>>、IEnumerable<IValidateOptions<TOptions>>

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

    再看下TOptions Create(string name)方法

    先遍历IEnumerable<IConfigureOptions<TOptions>>

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

    再遍历IEnumerable<IPostConfigureOptions<TOptions>>

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

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

    以上是创建IOptions<TOptions>的一个流程

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

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

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

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

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

    再来看下一些扩展方法

    /// <summary>
            /// Registers an action used to configure a particular type of options.
            /// Note: These are run before all <seealso cref="PostConfigure{TOptions}(IServiceCollection, Action{TOptions})"/>.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configured.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="configureOptions">The action used to configure the options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection Configure<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
                => services.Configure(Options.Options.DefaultName, configureOptions);
    
            /// <summary>
            /// Registers an action used to configure a particular type of options.
            /// Note: These are run before all <seealso cref="PostConfigure{TOptions}(IServiceCollection, Action{TOptions})"/>.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configured.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="name">The name of the options instance.</param>
            /// <param name="configureOptions">The action used to configure the options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)
                where TOptions : class
            {
                if (services == null)
                {
                    throw new ArgumentNullException(nameof(services));
                }
    
                if (configureOptions == null)
                {
                    throw new ArgumentNullException(nameof(configureOptions));
                }
    
                services.AddOptions();
                services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));
                return services;
            }
    
            /// <summary>
            /// Registers an action used to configure all instances of a particular type of options.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configured.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="configureOptions">The action used to configure the options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection ConfigureAll<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
                => services.Configure(name: null, configureOptions: configureOptions);
    
            /// <summary>
            /// Registers an action used to initialize a particular type of options.
            /// Note: These are run after all <seealso cref="Configure{TOptions}(IServiceCollection, Action{TOptions})"/>.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configured.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="configureOptions">The action used to configure the options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection PostConfigure<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
                => services.PostConfigure(Options.Options.DefaultName, configureOptions);
    
            /// <summary>
            /// Registers an action used to configure a particular type of options.
            /// Note: These are run after all <seealso cref="Configure{TOptions}(IServiceCollection, Action{TOptions})"/>.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configure.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="name">The name of the options instance.</param>
            /// <param name="configureOptions">The action used to configure the options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection PostConfigure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)
                where TOptions : class
            {
                if (services == null)
                {
                    throw new ArgumentNullException(nameof(services));
                }
    
                if (configureOptions == null)
                {
                    throw new ArgumentNullException(nameof(configureOptions));
                }
    
                services.AddOptions();
                services.AddSingleton<IPostConfigureOptions<TOptions>>(new PostConfigureOptions<TOptions>(name, configureOptions));
                return services;
            }
    
            /// <summary>
            /// Registers an action used to post configure all instances of a particular type of options.
            /// Note: These are run after all <seealso cref="Configure{TOptions}(IServiceCollection, Action{TOptions})"/>.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configured.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="configureOptions">The action used to configure the options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection PostConfigureAll<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class
                => services.PostConfigure(name: null, configureOptions: configureOptions);
    
            /// <summary>
            /// Registers a type that will have all of its I[Post]ConfigureOptions registered.
            /// </summary>
            /// <typeparam name="TConfigureOptions">The type that will configure options.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            public static IServiceCollection ConfigureOptions<TConfigureOptions>(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<Type> 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;
            }
    
            /// <summary>
            /// Registers a type that will have all of its I[Post]ConfigureOptions registered.
            /// </summary>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="configureType">The type that will configure options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            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;
            }
    
            /// <summary>
            /// Registers an object that will have all of its I[Post]ConfigureOptions registered.
            /// </summary>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="configureInstance">The instance that will configure options.</param>
            /// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
            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;
            }
    
            /// <summary>
            /// Gets an options builder that forwards Configure calls for the same <typeparamref name="TOptions"/> to the underlying service collection.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configured.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <returns>The <see cref="OptionsBuilder{TOptions}"/> so that configure calls can be chained in it.</returns>
            public static OptionsBuilder<TOptions> AddOptions<TOptions>(this IServiceCollection services) where TOptions : class
                => services.AddOptions<TOptions>(Options.Options.DefaultName);
    
            /// <summary>
            /// Gets an options builder that forwards Configure calls for the same named <typeparamref name="TOptions"/> to the underlying service collection.
            /// </summary>
            /// <typeparam name="TOptions">The options type to be configured.</typeparam>
            /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
            /// <param name="name">The name of the options instance.</param>
            /// <returns>The <see cref="OptionsBuilder{TOptions}"/> so that configure calls can be chained in it.</returns>
            public static OptionsBuilder<TOptions> AddOptions<TOptions>(this IServiceCollection services, string name)
                where TOptions : class
            {
                if (services == null)
                {
                    throw new ArgumentNullException(nameof(services));
                }
    
                services.AddOptions();
                return new OptionsBuilder<TOptions>(services, name);
            }
     
  • 相关阅读:
    求整数数组(长度为n),出现大于2/n次数的数字
    Job-Show Liang,你来掌管诺基亚王国,可好?
    wp面试题
    启动 Windows Phone 8 内置应用的 URI 方案
    .Net高级面试宝典
    JDK和Tomcat部署
    HAproxy功能配置
    配置HAProxy支持https协议
    Tomcat Cluster负载均衡
    Tomcat会话保持之session server
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12540260.html
Copyright © 2011-2022 走看看