配置的本質就是字元串的鍵值對,但是對於面向對象語言來說,能使用強類型的配置是何等的爽哉! 目錄 1. ASP.NET Core 配置系統 2. 強類型的 Options 3. Configure 方法 4. 源碼解析 ASP.NET Core 配置系統 在ASP.NET 4.X中,通常將配置存儲在 ...
配置的本質就是字元串的鍵值對,但是對於面向對象語言來說,能使用強類型的配置是何等的爽哉!
目錄
- ASP.NET Core 配置系統
- 強類型的 Options
- Configure 方法
- 源碼解析
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 方法是在什麼時候調用的呢 ”,且看下回分解。
總結
本文描述了在 .NET Core 配置系統中Options的配置及原理,在下一章來講一下IOptions
的使用。