asp.net core 系列 12 选项 TOptions
一.概述
本章讲的选项模式是对Configuration配置的功能扩展。 讲这篇时有个专用名词叫“选项类(TOptions)” 。该选项类作用是指:把选项类中的属性与配置来源中的键关联起来。举个例,假设json文件有个Option1键,选项类中也有个叫Option1的属性名,经过选项配置,这样就能把json中的键的值映射到选项类属性值中。也可以理解在项目应用中,把一个json文件序列化到.net类。
1.1选项接口介绍
在官方文档中选项接口有很多,这里列举了这些选项接口。所有选项接口基本都继承了TOptions接口。
// Used for notifications when TOptions instances change public interface IOptionsMonitor<out TOptions> { // // 摘要: // Returns the current TOptions instance with the Microsoft.Extensions.Options.Options.DefaultName. TOptions CurrentValue { get; } //... }</span><span style="color: #008000;">//</span><span style="color: #008000;"> Used to create TOptions instances</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IOptionsFactory<TOptions> <span style="color: #0000ff;">where</span> TOptions : <span style="color: #0000ff;">class</span>, <span style="color: #0000ff;">new</span><span style="color: #000000;">() { </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Returns a configured TOptions instance with the given name.</span> TOptions Create(<span style="color: #0000ff;">string</span><span style="color: #000000;"> name); } </span><span style="color: #008000;">//</span><span style="color: #008000;"> Represents something that configures the TOptions type. Note: These are run before all </span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IConfigureOptions<<span style="color: #0000ff;">in</span> TOptions> <span style="color: #0000ff;">where</span> TOptions : <span style="color: #0000ff;">class</span><span style="color: #000000;"> { </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Invoked to configure a TOptions instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 参数: </span><span style="color: #008000;">//</span><span style="color: #008000;"> options: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The options instance to configure.</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> Configure(TOptions options); } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IPostConfigureOptions<<span style="color: #0000ff;">in</span> TOptions> <span style="color: #0000ff;">where</span> TOptions : <span style="color: #0000ff;">class</span><span style="color: #000000;"> { </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Invoked to configure a TOptions instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 参数: </span><span style="color: #008000;">//</span><span style="color: #008000;"> name: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The name of the options instance being configured. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> options: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The options instance to configured.</span> <span style="color: #0000ff;">void</span> PostConfigure(<span style="color: #0000ff;">string</span><span style="color: #000000;"> name, TOptions options); } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IConfigureNamedOptions<<span style="color: #0000ff;">in</span> TOptions> : IConfigureOptions<TOptions> <span style="color: #0000ff;">where</span> TOptions : <span style="color: #0000ff;">class</span><span style="color: #000000;"> { </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Invoked to configure a TOptions instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 参数: </span><span style="color: #008000;">//</span><span style="color: #008000;"> name: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The name of the options instance being configured. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> options: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The options instance to configure.</span> <span style="color: #0000ff;">void</span> Configure(<span style="color: #0000ff;">string</span><span style="color: #000000;"> name, TOptions options); } </span><span style="color: #008000;">//</span><span style="color: #008000;"> Used by IOptionsMonitor<TOptions> to cache TOptions instances.</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IOptionsMonitorCache<TOptions> <span style="color: #0000ff;">where</span> TOptions : <span style="color: #0000ff;">class</span><span style="color: #000000;"> { </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Clears all options instances from the cache.</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> Clear(); </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Gets a named options instance, or adds a new instance created with createOptions. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 参数: </span><span style="color: #008000;">//</span><span style="color: #008000;"> name: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The name of the options instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> createOptions: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The func used to create the new instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 返回结果: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The options instance.</span> TOptions GetOrAdd(<span style="color: #0000ff;">string</span> name, Func<TOptions><span style="color: #000000;"> createOptions); </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Tries to adds a new option to the cache, will return false if the name already </span><span style="color: #008000;">//</span><span style="color: #008000;"> exists. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 参数: </span><span style="color: #008000;">//</span><span style="color: #008000;"> name: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The name of the options instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> options: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The options instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 返回结果: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Whether anything was added.</span> <span style="color: #0000ff;">bool</span> TryAdd(<span style="color: #0000ff;">string</span><span style="color: #000000;"> name, TOptions options); </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Try to remove an options instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 参数: </span><span style="color: #008000;">//</span><span style="color: #008000;"> name: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The name of the options instance. </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 返回结果: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Whether anything was removed.</span> <span style="color: #0000ff;">bool</span> TryRemove(<span style="color: #0000ff;">string</span><span style="color: #000000;"> name); } </span><span style="color: #008000;">//</span><span style="color: #008000;"> Used to access the value of TOptions for the lifetime of a request</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IOptionsSnapshot<<span style="color: #0000ff;">out</span> TOptions> : IOptions<TOptions> <span style="color: #0000ff;">where</span> TOptions : <span style="color: #0000ff;">class</span>, <span style="color: #0000ff;">new</span><span style="color: #000000;">() { </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> Returns a configured TOptions instance with the given name.</span> TOptions Get(<span style="color: #0000ff;">string</span><span style="color: #000000;"> name); } </span><span style="color: #008000;">//</span><span style="color: #008000;">Used to retrieve configured TOptions instances</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">interface</span> IOptions<<span style="color: #0000ff;">out</span> TOptions> <span style="color: #0000ff;">where</span> TOptions : <span style="color: #0000ff;">class</span>, <span style="color: #0000ff;">new</span><span style="color: #000000;">() { </span><span style="color: #008000;">//</span> <span style="color: #008000;">//</span><span style="color: #008000;"> 摘要: </span><span style="color: #008000;">//</span><span style="color: #008000;"> The default configured TOptions instance</span> TOptions Value { <span style="color: #0000ff;">get</span><span style="color: #000000;">; } }</span></pre>
(1) IOptionsMonitor<TOptions>
IOptionsMonitor<TOptions>用于TOptions实例更改时的通知,用于管理 TOptions选项类 。该IOptionsMonitor<TOptions>支持以下方案:
(1) 更改通知。当配置文件发生修改时,会监听同步到选项类。
(2) 命名选项。IConfigureNamedOptions支持命名选项。接口继续关系 IConfigureNamedOptions:IConfigureOptions
(3) 重新加载配置。通过 IOptionsSnapshot 重新加载配置数据,IOptionsMonitor也支持该功能。 接口继续关系IOptionsSnapshot :IOptions
(4) 选择性选项失效 (IOptionsMonitorCache<TOptions>)。
(2) 其它接口
IOptionsFactory<TOptions> 负责产生TOptions选项实例,它具有单个 Create 方法。
默认实现采用所有已注册 IConfigureOptions<TOptions> 和 IPostConfigureOptions<TOptions> 并首先运行所有配置(所有的来源配置),
然后才进行选项后期配置IPostConfigureOptions<TOptions> 。
IOptionsMonitorCache<TOptions>用于缓存TOptions实例。
1.2 常规选项配置
TOptions选项类必须为包含公共无参数构造函数的非抽象类。下面示例中选项类MyOptions具有两种属性:Option1
和 Option2
。 设置默认值为可选,但以下示例中的类构造函数设置了 Option1
的默认值。 Option2
具有通过直接初始化属性设置的默认值。
public class MyOptions { public MyOptions() { // Set default value. Option1 = "value1_from_ctor"; }</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> Option1 { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> Option2 { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span>; } = <span style="color: #800080;">5</span><span style="color: #000000;">; }</span></pre>
//将MyOptions类已通过Configure添加到服务容器,并绑定到配置IConfiguration上 //Registers a configuration instance which TOptions will bind against. services.Configure<MyOptions>(Configuration);
private readonly MyOptions _options;
//OtherPages/Page1 public Page1Model( IOptionsMonitor<MyOptions> optionsAccessor) { _options = optionsAccessor.CurrentValue; } public void OnGet() { var option1 = _options.Option1; var option2 = _options.Option2; var SimpleOptions = $"option1 = {option1}, option2 = {option2}"; }
// 此时SimpleOptions值:"option1 = value1_from_ctor, option2 = 5"
//下面在appsettings.json 文件添加 option1 和 option2 的值。 "option1": "value1_from_json", "option2": -1,
当配置文件appsettings.json中option1和option2键的值改变后,MyOptions 选项类中的属性option1和option2的值也会改变。 下面再次运行OtherPages/Page1
//此时SimpleOptions的值为:"option1 = value1_from_json, option2 = -1"
为什么MyOptions类中option1,option2会取到appsettings.json文件中的值呢?因为MyOptions选项类注入到Iconfiguration服务后与appsettings.json文件中键相同,形成了映射。
1.3 通过委托配置简单选项
使用委托设置选项值。 此示例应用使用 MyOptionsWithDelegateConfig 类 ,这里仍然是使用相同的键Option1,Option2。 它通过 MyOptionsWithDelegateConfig 使用委托来配置绑定。
public class MyOptionsWithDelegateConfig { public MyOptionsWithDelegateConfig() { // Set default value. Option1 = "value1_from_ctor"; }</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> Option1 { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> Option2 { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span>; } = <span style="color: #800080;">5</span><span style="color: #000000;">;
}
services.Configure<MyOptions>(Configuration); //第二个注入服务 services.Configure<MyOptionsWithDelegateConfig>(myOptions => {
// 在Page1Model构造方法中生成MyOptionsWithDelegateConfig实例时调用 myOptions.Option1 = "value1_configured_by_delegate"; myOptions.Option2 = 500; });
public Page1Model( IConfiguration configuration, IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, IOptionsMonitor<MyOptions> optionsAccessor ) {_optionsWithDelegateConfig </span>=<span style="color: #000000;"> optionsAccessorWithDelegateConfig.CurrentValue; _options </span>=<span style="color: #000000;"> optionsAccessor.CurrentValue; Configuration </span>=<span style="color: #000000;"> configuration; } </span><span style="color: #0000ff;">public</span> IConfiguration Configuration { <span style="color: #0000ff;">get</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">readonly</span><span style="color: #000000;"> MyOptions _options; </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">readonly</span><span style="color: #000000;"> MyOptionsWithDelegateConfig _optionsWithDelegateConfig; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> BindGUIDMsg { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> OnGet() { BindGUIDMsg </span>= $<span style="color: #800000;">"</span><span style="color: #800000;">option1 = {_options.Option1}, option2 = {_options.Option2}</span><span style="color: #800000;">"</span><span style="color: #000000;">; BindGUIDMsg </span>+= <span style="color: #800000;">"</span><span style="color: #800000;"></br></span><span style="color: #800000;">"</span><span style="color: #000000;">; BindGUIDMsg </span>+= $<span style="color: #800000;">"</span><span style="color: #800000;">delegate_option1 = {_optionsWithDelegateConfig.Option1}, </span><span style="color: #800000;">"</span> +<span style="color: #000000;"> $</span><span style="color: #800000;">"</span><span style="color: #800000;">delegate_option2 = {_optionsWithDelegateConfig.Option2}</span><span style="color: #800000;">"</span><span style="color: #000000;">; }</span></pre>
每次调用 Configure 都会将 IConfigureOptions<TOptions> 服务添加到服务容器。 在前面的示例中,Option1 和 Option2 的值同时在 appsettings.json 中指定,但 Option1 和 Option2 的值被配置的委托替代。
当启用多个配置服务时,指定的最后一个配置源优于其他源,由其设置配置值。 运行应用时,页面模型的 OnGet
方法返回显示选项类值的字符串:
// myOptions实例取值如下: SimpleOptions:option1 = -1,option2 = 5 // MyOptionsWithDelegateConfig实例取值如下: delegate_option1 = value1_configured_by_delegate,delegate_option2 = 500
1.4 子选项配置
当配置文件中的子节点需要与选项类进行关联映射时,可以通过服务注入bind到指定的Configuration节点。在以下代码中,已向服务容器添加第三个 IConfigureOptions<TOptions> 服务。 它将 MySubOptions 绑定到 appsettings.json 文件的 subsection 部分:
"Option1": "value1_from_json", "Option2": -1, "subsection": { "suboption1": "subvalue1_from_json", "suboption2": 200 }
public class MySubOptions { public MySubOptions() { // Set default values. SubOption1 = "value1_from_ctor"; SubOption2 = 5; }</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">string</span> SubOption1 { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> SubOption2 { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }
}
//第三个IConfigureOptions<TOptions>注入服务 services.Configure<MySubOptions>(Configuration.GetSection("subsection"));
//OtherPages/Page1 public Page1Model( IConfiguration configuration, IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, IOptionsMonitor<MyOptions> optionsAccessor, IOptionsMonitor<MySubOptions> subOptionsAccessor ) {_optionsWithDelegateConfig </span>=<span style="color: #000000;"> optionsAccessorWithDelegateConfig.CurrentValue; _options </span>=<span style="color: #000000;"> optionsAccessor.CurrentValue; _subOptions </span>=<span style="color: #000000;"> subOptionsAccessor.CurrentValue; Configuration </span>=<span style="color: #000000;"> configuration; } </span><span style="color: #0000ff;">public</span> IConfiguration Configuration { <span style="color: #0000ff;">get</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">readonly</span><span style="color: #000000;"> MyOptions _options; </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">readonly</span><span style="color: #000000;"> MyOptionsWithDelegateConfig _optionsWithDelegateConfig; </span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">readonly</span> MySubOptions _subOptions;</pre>
// OnGet方法 SubOptions = $"subOption1 = {_subOptions.SubOption1}, subOption2 = {_subOptions.SubOption2}"
//运行应用时,OnGet 方法返回显示子选项类值的字符串: // MySubOptions实例取值如下: subOption1 = subvalue1_from_json,subOption2 = 200
1.5 IConfigureNamedOptions选项命名
IConfigureNamedOptions:是用于对选项类在注入服务时,进行命名。下面是把MyOptions选项类注入到服务,并取了别名named_options_1。 如下所示:
services.Configure<MyOptions>("named_options_1", Configuration);
//在要使用的页面,如下所示: private readonly MyOptions _named_options_1; public IndexModel( IOptionsSnapshot<MyOptions> namedOptionsAccessor) { _named_options_1 = namedOptionsAccessor.Get("named_options_1"); }
// IOptionsSnapshot源码 public interface IOptionsSnapshot<out TOptions> : IOptions<TOptions> where TOptions : class, new() { // // 摘要: // Returns a configured TOptions instance with the given name. TOptions Get(string name); }
1.6 ConfigureAll
上面介绍的选项类,在注入服务时都是使用的services.Configure,它是针对选项类的当前实例,例如:在page1和page2都使用了实例MyOptions( private readonly MyOptions _options)但它们属于不同的实例。
//使用 ConfigureAll 方法配置所有选项实例 services.ConfigureAll<MyOptions>(myOptions => { //所有MyOptions实例配置 Option1的值 myOptions.Option1 = "ConfigureAll replacement value"; });
//添加代码后运行示例应用将产生以下结果: named_options_1: option1 = ConfigureAll replacement value, option2 = -1 named_options_2: option1 = ConfigureAll replacement value, option2 = 5
总结:
选项模式是对Configuration配置的功能扩展,主要用于把json文件序列化到.net类(选项类)。通过注入services. Configure或services.ConfigureAll把选项类注入到服务并绑定到Configuration上。 选项这篇还有其它功能如:选项后期配置( IPostConfigureOptions), 启动期间访问选项, 选项验证等,详细了解查看官网
参考文献
官方资料:asp.net core 选项