如何使用.NET Core 选项模式【Options】
ASP.NETCore引入了Options模式,使用类来表示相关的设置组。简单的来说,就是用强类型的类来表达配置项,这带来了很多好处。利用了系统的依赖注入,并且还可以利用配置系统。它使我们可以采用依赖注入的方法直接使用绑定的一个POCO对象,这个POCO对象就叫做Options对象。也可以叫做配置对象。
以下大多内容来自官方文档,我只是个翻译官或者叫搬运工吧!
引入Options扩展包
PM>Package-installMicrosoft.Extensions.Options
绑定分层配置
在appsetting.json文件增加如下配置
"Position":{ "Title":"Editor", "Name":"JoeSmith" }
创建以下PositionOptions类:
publicclassPositionOptions { publicconststringPosition="Position"; publicstringTitle{get;set;} publicstringName{get;set;} }
选项类:
- 必须是包含公共无参数构造函数的非抽象类。
- 类型的所有公共读写属性都已绑定。
- 不会绑定字段。在上面的代码中,Position未绑定。由于使用了Position属性,因此在将类绑定到配置提供程序时,不需要在应用中对字符串"Position"进行硬编码。
类绑定
调用ConfigurationBinder.Bind将PositionOptions类绑定到Position部分。然后就可以用了,当然这种方式在开发.NETCore种并不常用,一般采用依赖注入的方式注入。
varpositionOptions=newPositionOptions(); Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
使用ConfigurationBinder.Get可能比使用ConfigurationBinder.Bind更方便。
positionOptions=Configuration.GetSection(PositionOptions.Position).Get();
依赖项注入服务容器
修改ConfigureServices方法
publicvoidConfigureServices(IServiceCollectionservices) { services.Configure(Configuration.GetSection( PositionOptions.Position)); services.AddRazorPages(); }
通过使用前面的代码,以下代码将读取位置选项:
publicclassTest2Model:PageModel { privatereadonlyPositionOptions_options; publicTest2Model(IOptionsoptions) { _options=options.Value; } publicContentResultOnGet() { returnContent($"Title:{_options.Title}\n"+ $"Name:{_options.Name}"); } }
选项接口
初学者会发现这个框架有3个主要的面向消费者的接口:IOptions、IOptionsMonitor以及IOptionsSnapshot。
这三个接口初看起来很类似,所以很容易引起困惑,什么场景下该用哪个接口呢?
1.IOptions
- 不支持
(1)在应用启动后读取配置数据。
(2)命名选项
- 注册为单一实例,可以注入到任何服务生存期。
2.IOptionsSnapshot
- 作用域容器配置热更新使用它
- 注册为范围内,因此无法注入到单一实例服务
- 支持命名选项
3.IOptionsMonitor
- 用于检索选项并管理TOptions实例的选项通知。
- 注册为单一实例且可以注入到任何服务生存期。
- 支持
(1)更改通知
(2)命名选项
(3)可重载配置
(4)选择性选项失效
使用IOptionsSnapshot读取已更新的数据
IOptionsMonitor和IOptionsSnapshot之间的区别在于:
- IOptionsMonitor是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。
- IOptionsSnapshot是一种作用域服务,并在构造IOptionsSnapshot对象时提供选项的快照。选项快照旨在用于暂时性和有作用域的依赖项。
publicclassTestSnapModel:PageModel { privatereadonlyMyOptions_snapshotOptions; publicTestSnapModel(IOptionsSnapshotsnapshotOptionsAccessor) { _snapshotOptions=snapshotOptionsAccessor.Value; } publicContentResultOnGet() { returnContent($"Option1:{_snapshotOptions.Option1}\n"+ $"Option2:{_snapshotOptions.Option2}"); } }
IOptionsMonitor
publicclassTestMonitorModel:PageModel { privatereadonlyIOptionsMonitor_optionsDelegate; publicTestMonitorModel(IOptionsMonitor optionsDelegate) { _optionsDelegate=optionsDelegate; } publicContentResultOnGet() { returnContent($"Option1:{_optionsDelegate.CurrentValue.Option1}\n"+ $"Option2:{_optionsDelegate.CurrentValue.Option2}"); } }
命名选项支持使用IConfigureNamedOptions
命名选项:
- 当多个配置节绑定到同一属性时有用。
- 区分大小写。
appsettings.json文件
{ "TopItem":{ "Month":{ "Name":"GreenWidget", "Model":"GW46" }, "Year":{ "Name":"OrangeGadget", "Model":"OG35" } } }
下面的类用于每个节,而不是创建两个类来绑定TopItem:Month和TopItem:Year
publicclassTopItemSettings { publicconststringMonth="Month"; publicconststringYear="Year"; publicstringName{get;set;} publicstringModel{get;set;} }
依赖注入容器
publicvoidConfigureServices(IServiceCollectionservices) { services.Configure(TopItemSettings.Month, Configuration.GetSection("TopItem:Month")); services.Configure (TopItemSettings.Year, Configuration.GetSection("TopItem:Year")); services.AddRazorPages(); }
服务应用
publicclassTestNOModel:PageModel { privatereadonlyTopItemSettings_monthTopItem; privatereadonlyTopItemSettings_yearTopItem; publicTestNOModel(IOptionsSnapshotnamedOptionsAccessor) { _monthTopItem=namedOptionsAccessor.Get(TopItemSettings.Month); _yearTopItem=namedOptionsAccessor.Get(TopItemSettings.Year); } }
使用DI服务配置选项
在配置选项时,可以通过以下两种方式通过依赖关系注入访问服务:
- 将配置委托传递给OptionsBuilder上的Configure
services.AddOptions("optionalName") .Configure ( (o,s,s2,s3,s4,s5)=> o.Property=DoSomethingWith(s,s2,s3,s4,s5));
建议将配置委托传递给Configure,因为创建服务较复杂。在调用Configure时,创建类型等效于框架执行的操作。调用Configure会注册临时泛型IConfigureNamedOptions,它具有接受指定的泛型服务类型的构造函数。
选项验证
appsettings.json文件
{ "MyConfig":{ "Key1":"MyKeyOne", "Key2":10, "Key3":32 } }
下面的类绑定到"MyConfig"配置节,并应用若干DataAnnotations规则:
publicclassMyConfigOptions { publicconststringMyConfig="MyConfig"; [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")] publicstringKey1{get;set;} [Range(0,1000, ErrorMessage="Valuefor{0}mustbebetween{1}and{2}.")] publicintKey2{get;set;} publicintKey3{get;set;} }
- 启用DataAnnotations验证
publicvoidConfigureServices(IServiceCollectionservices) { services.AddOptions() .Bind(Configuration.GetSection(MyConfigOptions.MyConfig)) .ValidateDataAnnotations(); services.AddControllersWithViews(); }
使用IValidateOptions更复杂的配置
publicclassMyConfigValidation:IValidateOptions{ publicMyConfigOptions_config{get;privateset;} publicMyConfigValidation(IConfigurationconfig) { _config=config.GetSection(MyConfigOptions.MyConfig) .Get (); } publicValidateOptionsResultValidate(stringname,MyConfigOptionsoptions) { stringvor=null; varrx=newRegex(@"^[a-zA-Z''-'\s]{1,40}$"); varmatch=rx.Match(options.Key1); if(string.IsNullOrEmpty(match.Value)) { vor=$"{options.Key1}doesn'tmatchRegEx\n"; } if(options.Key2<0||options.Key2>1000) { vor=$"{options.Key2}doesn'tmatchRange0-1000\n"; } if(_config.Key2!=default) { if(_config.Key3<=_config.Key2) { vor+="Key3mustbe>thanKey2."; } } if(vor!=null) { returnValidateOptionsResult.Fail(vor); } returnValidateOptionsResult.Success; } }
IValidateOptions允许将验证代码移出StartUp并将其移入类中。
使用前面的代码,使用以下代码在Startup.ConfigureServices中启用验证
publicvoidConfigureServices(IServiceCollectionservices) { services.Configure(Configuration.GetSection( MyConfigOptions.MyConfig)); services.TryAddEnumerable(ServiceDescriptor.Singleton ,MyConfigValidation>()); services.AddControllersWithViews(); }
选项后期配置
使用IPostConfigureOptions设置后期配置。进行所有IConfigureOptions配置后运行后期配置
services.PostConfigure(myOptions=> { myOptions.Option1="post_configured_option1_value"; });
使用PostConfigureAll对所有配置实例进行后期配置
在启动期间访问选项
IOptions和IOptionsMonitor可用于Startup.Configure中,因为在Configure方法执行之前已生成服务。
publicvoidConfigure(IApplicationBuilderapp, IOptionsMonitoroptionsAccessor) { varoption1=optionsAccessor.CurrentValue.Option1; }
结论
IOptions<>是单例,因此一旦生成了,除非通过代码的方式更改,它的值是不会更新的。
IOptionsMonitor<>也是单例,但是它通过IOptionsChangeTokenSource<>能够和配置文件一起更新,也能通过代码的方式更改值。
IOptionsSnapshot<>是范围,所以在配置文件更新的下一次访问,它的值会更新,但是它不能跨范围通过代码的方式更改值,只能在当前范围(请求)内有效。
所以你应该根据你的实际使用场景来选择到底是用这三者中的哪一个。
一般来说,如果你依赖配置文件,那么首先考虑IOptionsMonitor<>,如果不合适接着考虑IOptionsSnapshot<>,最后考虑IOptions<>。
有一点需要注意,在ASP.NETCore应用中IOptionsMonitor可能会导致同一个请求中选项的值不一致,当你正在修改配置文件的时候,这可能会引发一些奇怪的bug。
如果这个对你很重要,请使用IOptionsSnapshot,它可以保证同一个请求中的一致性,但是它可能会带来轻微的性能上的损失。
如果你是在app启动的时候自己构造Options(比如在Startup类中):
services.Configure(opt=>opt.Name="Test");
IOptions<>最简单,也许是一个不错的选择。
以上就是如何使用.NETCore选项模式【Options】的详细内容,更多关于.NETCore选项模式【Options】的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。