ASP.NET Core 选项模式源码学习Options Configure(一)

前言

ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户机密等 并可以通过应用程序中的相同界面轻松访问,除此之外,ASP.NET中的新配置系统允许使用Options的强类型设置。

强类型Options

在ASP.NET Core中没有AppSettings["Key"]默认方法,那么推荐的是创建强类型的配置类,去绑定配置项。

    public class MyOptions    {        public string Name { get; set; }        public string Url { get; set; }    }

然后我们在appsettings.json中添加如下内容:

{  "MyOptions":     {      "Name": "TestName",      "Url": "TestUrl"    }}

配置绑定到类

ConfigureServices方法进行配置以绑定到类

        public void ConfigureServices(IServiceCollection services)        {            services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));            services.AddControllers();        }

MyOptions只需将IOptions<>类的实例注入控制器中,然后通过Value属性获取Myoptions:

    public class WeatherForecastController : ControllerBase    {        private readonly MyOptions _options;        public WeatherForecastController(IOptions<MyOptions> options)        {            _options = options.Value;        }        [HttpGet]        public OkObjectResult Get() {            return Ok(string.Format("Name:{0},Url:{1}", _options.Name,_options.Url));        }    }

Configure

委托配置
            //基础注册方式            services.Configure<MyOptions>(o => { o.Url = "MyOptions"; o.Name = "Name111"; });            //指定具体名称            services.Configure<MyOptions>("Option", o => { o.Url = "MyOptions"; o.Name = "Name111"; }) ;            //配置所有实例            services.ConfigureAll<MyOptions>(options =>{ options.Name = "Name1";  options.Url = "Url1";});
通过配置文件配置
           // 使用配置文件来注册实例            services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));            // 指定具体名称            services.Configure<MyOptions>("Option", Configuration.GetSection("MyOptions"));
PostConfigure

PostConfigure会在Configure注册完之后再进行注册

     services.PostConfigure<MyOptions>(o => o.Name = "Name1");            services.PostConfigure<MyOptions>("Option", o => o.Name = "Name1");            services.PostConfigureAll<MyOptions>(o => o.Name = "Name1");

源码解析

IConfigureOptions接口

    public interface IConfigureOptions<in TOptions> where TOptions : class    {                void Configure(TOptions options);    }

Configure为方便使用IConfigureOptions注册单例ConfigureNamedOptions

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

上面代码IConfigureOptions实现了ConfigureNamedOptions,那我们再来看看内部源码
ConfigureNamedOptions 其实就是把我们注册的Action包装成统一的Configure方法,以方便后续创建Options实例时,进行初始化。

    public class ConfigureNamedOptions<TOptions> : IConfigureNamedOptions<TOptions> where TOptions : class    {                public ConfigureNamedOptions(string name, Action<TOptions> action)        {            Name = name;            Action = action;        }               public string Name { get; }              public Action<TOptions> Action { get; }              public virtual void Configure(string name, TOptions options)        {            if (options == null)            {                throw new ArgumentNullException(nameof(options));            }            // Null name is used to configure all named options.            if (Name == null || name == Name)            {                Action?.Invoke(options);            }        }        public void Configure(TOptions options) => Configure(Options.DefaultName, options);    }

services.Configure(Configuration.GetSection("MyOptions")); 我们不指定具体名称的时候默认是如下代码片段

        public virtual void Configure(string name, TOptions options)        {            if (options == null)            {                throw new ArgumentNullException(nameof(options));            }            // Null name is used to configure all named options.            if (Name == null || name == Name)            {                Action?.Invoke(options);            }        }        public void Configure(TOptions options) => Configure(Options.DefaultName, options);

默认使用的是Options.DefaultName

AddOptions默认方法默认为我们注册了一些核心的类

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

相关推荐