如何使用.NET Core 选项模式【Options】
ASP.NET Core引入了Options模式,使用类来表示相关的设置组。简单的来说,就是用强类型的类来表达配置项,这带来了很多好处。利用了系统的依赖注入,并且还可以利用配置系统。它使我们可以采用依赖注入的方法直接使用绑定的一个POCO对象,这个POCO对象就叫做Options对象。也可以叫做配置对象。
以下大多内容来自官方文档,我只是个翻译官或者叫搬运工吧!
引入Options扩展包
PM>Package-install Microsoft.Extensions.Options
绑定分层配置
在appsetting.json文件增加如下配置
"Position": { "Title": "Editor", "Name": "Joe Smith" }
创建以下 PositionOptions 类:
public class PositionOptions { public const string Position = "Position"; public string Title { get; set; } public string Name { get; set; } }
选项类:
- 必须是包含公共无参数构造函数的非抽象类。
- 类型的所有公共读写属性都已绑定。
- 不会绑定字段。 在上面的代码中,Position 未绑定。 由于使用了 Position 属性,因此在将类绑定到配置提供程序时,不需要在应用中对字符串 "Position" 进行硬编码。
类绑定
调用 ConfigurationBinder.Bind 将 PositionOptions 类绑定到 Position 部分。然后就可以用了,当然这种方式在开发.NET Core种并不常用,一般采用依赖注入的方式注入。
var positionOptions = new PositionOptions(); Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
使用 ConfigurationBinder.Get 可能比使用 ConfigurationBinder.Bind 更方便。
positionOptions = Configuration.GetSection(PositionOptions.Position).Get<PositionOptions>();
依赖项注入服务容器
修改ConfigureServices方法
public void ConfigureServices(IServiceCollection services) { services.Configure<PositionOptions>(Configuration.GetSection( PositionOptions.Position)); services.AddRazorPages(); }
通过使用前面的代码,以下代码将读取位置选项:
public class Test2Model : PageModel { private readonly PositionOptions _options; public Test2Model(IOptions<PositionOptions> options) { _options = options.Value; } public ContentResult OnGet() { return Content($"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 对象时提供选项的快照。 选项快照旨在用于暂时性和有作用域的依赖项。
public class TestSnapModel : PageModel { private readonly MyOptions _snapshotOptions; public TestSnapModel(IOptionsSnapshot<MyOptions> snapshotOptionsAccessor) { _snapshotOptions = snapshotOptionsAccessor.Value; } public ContentResult OnGet() { return Content($"Option1: {_snapshotOptions.Option1} \n" + $"Option2: {_snapshotOptions.Option2}"); } }
IOptionsMonitor
public class TestMonitorModel : PageModel { private readonly IOptionsMonitor<MyOptions> _optionsDelegate; public TestMonitorModel(IOptionsMonitor<MyOptions> optionsDelegate ) { _optionsDelegate = optionsDelegate; } public ContentResult OnGet() { return Content($"Option1: {_optionsDelegate.CurrentValue.Option1} \n" + $"Option2: {_optionsDelegate.CurrentValue.Option2}"); } }
命名选项支持使用 IConfigureNamedOptions
命名选项:
- 当多个配置节绑定到同一属性时有用。
- 区分大小写。
appsettings.json文件
{ "TopItem": { "Month": { "Name": "Green Widget", "Model": "GW46" }, "Year": { "Name": "Orange Gadget", "Model": "OG35" } } }
下面的类用于每个节,而不是创建两个类来绑定 TopItem:Month 和 TopItem:Year
public class TopItemSettings { public const string Month = "Month"; public const string Year = "Year"; public string Name { get; set; } public string Model { get; set; } }
依赖注入容器
public void ConfigureServices(IServiceCollection services) { services.Configure<TopItemSettings>(TopItemSettings.Month, Configuration.GetSection("TopItem:Month")); services.Configure<TopItemSettings>(TopItemSettings.Year, Configuration.GetSection("TopItem:Year")); services.AddRazorPages(); }
服务应用
public class TestNOModel : PageModel { private readonly TopItemSettings _monthTopItem; private readonly TopItemSettings _yearTopItem; public TestNOModel(IOptionsSnapshot<TopItemSettings> namedOptionsAccessor) { _monthTopItem = namedOptionsAccessor.Get(TopItemSettings.Month); _yearTopItem = namedOptionsAccessor.Get(TopItemSettings.Year); } }
使用 DI 服务配置选项
在配置选项时,可以通过以下两种方式通过依赖关系注入访问服务:
- 将配置委托传递给 OptionsBuilder 上的 Configure
services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
建议将配置委托传递给 Configure,因为创建服务较复杂。 在调用 Configure 时,创建类型等效于框架执行的操作。 调用 Configure 会注册临时泛型 IConfigureNamedOptions,它具有接受指定的泛型服务类型的构造函数。
选项验证
appsettings.json 文件
{ "MyConfig": { "Key1": "My Key One", "Key2": 10, "Key3": 32 } }
下面的类绑定到 "MyConfig" 配置节,并应用若干 DataAnnotations 规则:
public class MyConfigOptions { public const string MyConfig = "MyConfig"; [RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")] public string Key1 { get; set; } [Range(0, 1000, ErrorMessage = "Value for {0} must be between {1} and {2}.")] public int Key2 { get; set; } public int Key3 { get; set; } }
- 启用DataAnnotations验证
public void ConfigureServices(IServiceCollection services) { services.AddOptions<MyConfigOptions>() .Bind(Configuration.GetSection(MyConfigOptions.MyConfig)) .ValidateDataAnnotations(); services.AddControllersWithViews(); }
使用IValidateOptions更复杂的配置
public class MyConfigValidation : IValidateOptions<MyConfigOptions> { public MyConfigOptions _config { get; private set; } public MyConfigValidation(IConfiguration config) { _config = config.GetSection(MyConfigOptions.MyConfig) .Get<MyConfigOptions>(); } public ValidateOptionsResult Validate(string name, MyConfigOptions options) { string vor=null; var rx = new Regex(@"^[a-zA-Z''-'\s]{1,40}$"); var match = rx.Match(options.Key1); if (string.IsNullOrEmpty(match.Value)) { vor = $"{options.Key1} doesn't match RegEx \n"; } if ( options.Key2 < 0 || options.Key2 > 1000) { vor = $"{options.Key2} doesn't match Range 0 - 1000 \n"; } if (_config.Key2 != default) { if(_config.Key3 <= _config.Key2) { vor += "Key3 must be > than Key2."; } } if (vor != null) { return ValidateOptionsResult.Fail(vor); } return ValidateOptionsResult.Success; } }
IValidateOptions 允许将验证代码移出 StartUp 并将其移入类中。
使用前面的代码,使用以下代码在 Startup.ConfigureServices 中启用验证
public void ConfigureServices(IServiceCollection services) { services.Configure<MyConfigOptions>(Configuration.GetSection( MyConfigOptions.MyConfig)); services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions <MyConfigOptions>, MyConfigValidation>()); services.AddControllersWithViews(); }
选项后期配置
使用 IPostConfigureOptions 设置后期配置。进行所有 IConfigureOptions 配置后运行后期配置
services.PostConfigure<MyOptions>(myOptions => { myOptions.Option1 = "post_configured_option1_value"; });
使用 PostConfigureAll 对所有配置实例进行后期配置
在启动期间访问选项
IOptions 和 IOptionsMonitor 可用于 Startup.Configure 中,因为在 Configure 方法执行之前已生成服务。
public void Configure(IApplicationBuilder app, IOptionsMonitor<MyOptions> optionsAccessor) { var option1 = optionsAccessor.CurrentValue.Option1; }
结论
IOptions<>是单例,因此一旦生成了,除非通过代码的方式更改,它的值是不会更新的。
IOptionsMonitor<>也是单例,但是它通过IOptionsChangeTokenSource<> 能够和配置文件一起更新,也能通过代码的方式更改值。
IOptionsSnapshot<>是范围,所以在配置文件更新的下一次访问,它的值会更新,但是它不能跨范围通过代码的方式更改值,只能在当前范围(请求)内有效。
所以你应该根据你的实际使用场景来选择到底是用这三者中的哪一个。
一般来说,如果你依赖配置文件,那么首先考虑IOptionsMonitor<>,如果不合适接着考虑IOptionsSnapshot<>,最后考虑IOptions<>。
有一点需要注意,在ASP.NET Core应用中IOptionsMonitor可能会导致同一个请求中选项的值不一致,当你正在修改配置文件的时候,这可能会引发一些奇怪的bug。
如果这个对你很重要,请使用IOptionsSnapshot,它可以保证同一个请求中的一致性,但是它可能会带来轻微的性能上的损失。
如果你是在app启动的时候自己构造Options(比如在Startup类中):
services.Configure<TestOptions>(opt => opt.Name = "Test");
IOptions<>最简单,也许是一个不错的选择。
以上就是如何使用.NET Core 选项模式【Options】的详细内容,更多关于.NET Core 选项模式【Options】的资料请关注猪先飞其它相关文章!
相关文章
- 这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
- 这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
- 在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
- 这篇文章主要介绍了Win10 IIS 安装及.net 4.5及Win10安装IIS并配置ASP.NET 4.0的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
- 这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
- 这篇文章主要介绍了.net数据库操作框架SqlSugar的简单入门,帮助大家更好的理解和学习使用.net技术,感兴趣的朋友可以了解下...2021-09-22
ASP.NET Core根据环境变量支持多个 appsettings.json配置文件
这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22- 这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
详解ASP.NET Core 中基于工厂的中间件激活的实现方法
这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22- 这篇文章主要介绍了C#使用Ado.Net更新和添加数据到Excel表格的方法,较为详细的分析了OLEDB的原理与使用技巧,可实现较为方便的操作Excel数据,需要的朋友可以参考下...2020-06-25
- ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。这篇文章主要给大家介绍了.NET C#利用ZXing生成、识别二维码/条形码的方法,文中给出了详细的示例代码,有需要的朋友们可以参考借鉴。...2020-06-25
asp.net通过消息队列处理高并发请求(以抢小米手机为例)
这篇文章主要介绍了asp.net通过消息队列处理高并发请求(以抢小米手机为例),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22ASP.NET单选按钮控件RadioButton常用属性和方法介绍
RadioButton又称单选按钮,其在工具箱中的图标为 ,单选按钮通常成组出现,用于提供两个或多个互斥选项,即在一组单选钮中只能选择一个...2021-09-22- Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
ASP.NET 2.0中的数据操作:使用两个DropDownList过滤的主/从报表
在前面的指南中我们研究了如何显示一个简单的主/从报表, 该报表使用DropDownList和GridView控件, DropDownList填充类别,GridView显示选定类别的产品. 这类报表用于显示具有...2016-05-19详解.NET Core 使用HttpClient SSL请求出错的解决办法
这篇文章主要介绍了.NET Core 使用HttpClient SSL请求出错的解决办法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22- 这篇文章主要介绍了Python调用.NET库的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-05-09
ASP.NET中iframe框架点击左边页面链接 右边显示链接页面内容
这篇文章主要介绍了ASP.NET中iframe框架点击左边页面链接,右边显示链接页面内容的实现代码,感兴趣的小伙伴们可以参考一下...2021-09-22- ASP.NET Web API具有与ASP.NET MVC类似的编程方式,ASP.NET Web API不仅仅具有一个完全独立的消息处理管道,而且这个管道比为ASP.NET MVC设计的管道更为复杂,功能也更为强大。下面创建一个简单的Web API项目,需要的朋友可以参考下...2021-09-22
- 这篇文章主要介绍了ASP.NET连接MySql数据库的2个方法及示例,使用的是MySQL官方组件和ODBC.NET,需要的朋友可以参考下...2021-09-22