ASP.NET Core 源码学习之 Options[1]:Configure
2017-08-08 07:12
1031 查看
ASP.NET Core 配置系统
在ASP.NET 4.X中,通常将配置存储在web.config中,使用静态帮助类来获取这些配置,而对
web.cofng中进行任何修改时,则会导致应用程序池的回收,这种实现方式并不是很友好。
因此,在ASP.NET Core中,对配置系统进行了重写,仍然使用的是基本的键值对,但是它们可以从多种格式的配置源中来获取,比如:命令行、环境变量、XML文件、JSON文件等等,你也可以编写自定义的配置源提供程序。
通常在
Stratup类的构造函数中对配置源进行设置:
public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; }
首先创建了一个
ConfigurationBuilder,然后设置配置源,最终生成的
Configuration是所有配置源中的键值对组合。
你可以直接在程序中使用
IConfigurationRoot来读取配置,但是建议你使用强类型的
Options,这样在你想获取某个配置时,只需要注入对应的
Options,而不是获取整个配置。
强类型的 Options
Options is a framework for accessing and configuring POCO settings.简单来说,Options 就是将一个 POCO 的配置类,通过在
Startup类中注册到容器中,在后续使用的时候使用构造函数注入来获取到POCO对象。我们将这种编程模式称为Options模式。
首先定义一个 Options:
public class MyOptions{ public string DefaultValue { get; set; } }
然后我们在对应的
appsettings.json中添加如下片段:
{ "MyOptions": { "DefaultValue" : "first" }}
在
Startup中的
ConfigureServices方法中,进行服务的注册:
public void ConfigureServices(IServiceCollection services) { services.Configure<MyOptions>(Configuration.GetSection("MyOptions")); }
最后,便在控制器中注入
IOptions<MyOptions>,通过其
Value属性对
MyOptions进行访问:
[Route("api/[controller]")] public class ValuesController : Controller { private readonly MyOptions _options; public ValuesController(IOptions<MyOptions> options) { _options = options.Value; } [HttpGet] public string Get() { return _options.DefaultValue; } }
Configure 方法
Options框架为我们提供了一系统的IServiceCollection的扩展方法,方便我们的使用。
直接使用Action配置
// 最简单的注册方式services.Configure<MyOptions>(o => o.DefaultValue = true);// 指定具体名称services.Configure<MyOptions>("my", o => o.DefaultValue = true);// 配置所有实例services.ConfigureAll<MyOptions>(o => o.DefaultValue = true);
通过配置文件进行配置
// 使用配置文件来注册实例services.Configure<MyOptions>(Configuration.GetSection("Sign"));// 指定具体名称services.Configure<MyOptions>("my", Configuration.GetSection("Sign"));// 配置所有实例services.ConfigureAll<MyOptions>(Configuration.GetSection("Sign"));
PostConfigure方法
PostConfigure方法在
Configure方法之后执行,是2.0中新增加的。
services.PostConfigure<MyOptions>(o => o.DefaultValue = true); services.PostConfigure<MyOptions>("smyign", o => o.DefaultValue = true); services.PostConfigureAll<MyOptions>(o => o.DefaultValue = true);
源码解析
首先看一下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.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions)); return services; }
而不指定
name的Configure和ConfigureAll方法,都只是一种简写形式,使用默认的
name:
public static class Options{ public static readonly string DefaultName = string.Empty; } public static IServiceCollection Configure<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class => services.Configure(Options.Options.DefaultName, configureOptions); public static IServiceCollection ConfigureAll<TOptions>(this IServiceCollection services, Action<TOptions> configureOptions) where TOptions : class=> services.Configure(name: null, configureOptions: configureOptions);
Configure方法其实就是为
IConfigureOptions<>注册了一个单例
ConfigureNamedOptions<>。
再看一下使用
IConfiguration进行配置的扩展方法:
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, IConfiguration config) where TOptions : class{ if (services == null) { throw new ArgumentNullException(nameof(services)); } if (config == null) { throw new ArgumentNullException(nameof(config)); } services.AddSingleton<IOptionsChangeTokenSource<TOptions>>(new ConfigurationChangeTokenSource<TOptions>(name, config)); return services.AddSingleton<IConfigureOptions<TOptions>>(new NamedConfigureFromConfigurationOptions<TOptions>(name, config)); }
可以看到,注册的实例变成了
NamedConfigureFromConfigurationOptions,而其本质依然是调用
ConfigureNamedOptions,只不过
Action的方法体变成了
ConfigurationBinder.Bind():
public class NamedConfigureFromConfigurationOptions<TOptions> : ConfigureNamedOptions<TOptions> where TOptions : class{ public NamedConfigureFromConfigurationOptions(string name, IConfiguration config) : base(name, options => ConfigurationBinder.Bind(config, options)) { if (config == null) { throw new ArgumentNullException(nameof(config)); } } }
大家或许会有点疑问:“ IConfigureOptions 中的 Configure 方法是在什么时候调用的呢 ”,这个且看下章分解。
ConfigureNamedOptions
ConfigureNamedOptions 实现了IConfigureNamedOptions,而
IConfigureNamedOptions则是对
IConfigureOptions的一个扩展,添加了
Name参数,这样,我们便可以为同一个 Options 类型注册多个独立的实例,在某些场景下则是非常有用的。有关
Name的使用,则会在 第三章 来讲。
public interface IConfigureNamedOptions<in TOptions> : IConfigureOptions<TOptions> where TOptions : class{ void Configure(string name, TOptions options); }
再看一下 ConfigureNamedOptions 的源码:
public class ConfigureNamedOptions<TOptions> : IConfigureNamedOptions<TOptions>, IConfigureOptions<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)); } if (Name == null || name == Name) { Action?.Invoke(options); } } public void Configure(TOptions options) => Configure(Options.DefaultName, options); }
ConfigureNamedOptions 本质上就是把我们注册的
Action包装成统一的
Configure方法,以方便后续创建
Options实例时,进行初始化。
总结
本文描述了在 .NET Core 配置系统中Options的配置及原理,在 下一章 来讲一下IOptions的
相关文章推荐
- ASP.NET Core 2.1 源码学习之 Options[1]:Configure
- ASP.NET Core 2.1 源码学习之 Options[3]:IOptionsMonitor
- ASP.NET Core 源码学习之 Options[2]:IOptions
- ASP.NET Core 2.1 源码学习之 Options[2]:IOptions
- ASP.NET Core 源码学习之 Options[3]:IOptionsSnapshot
- ASP.NET Core 源码学习之 Logging[2]:Configure
- ASP.NET Core 源码学习之 Logging[2]:Configure
- ASP.NET Core 源码学习之 Options[4]:IOptionsMonitor
- ASP.NET Core 源码学习之 Logging[1]:Introduction
- 【ASP.NET Core 】ASP.NET Core 源码学习之 Logging[1]:Introduction
- ASP.NET Core 源码学习之Logging[1]:Introduction
- asp.net core源码飘香:Options组件
- ASP.NET Core 源码学习之 Logging[3]:Logger
- ASP.NET Core 源码学习之 Logging[4]:FileProvider
- Asp.net core 学习笔记 ( Area and Feature folder structure 文件结构 )
- (入门整理学习一)Asp.net core
- asp.net core轻松入门之MVC中Options读取配置文件
- C# ASP。NET 源码 学习 下载机 泪流满面
- ASP.NET Core学习链接
- 【.NetCore学习】ASP.NET Core EF Core2.0 DB First现有数据库自动生成实体Context