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. IOptionsSnapshot
  • 作用域容器配置热更新使用它
  • 注册为范围内,因此无法注入到单一实例服务
  • 支持命名选项
  1. IOptionsMonitor
  • 用于检索选项并管理 TOptions 实例的选项通知。
  • 注册为单一实例且可以注入到任何服务生存期。
  • 支持
    • 更改通知
    • 命名选项
    • 可重载配置
    • 选择性选项失效

使用 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));
  • 创建实现 IConfigureOptions 或 IConfigureNamedOptions 的类型,并将该类型注册为服务

建议将配置委托传递给 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】的使用的更多相关文章

  1. .Net Core之选项模式Options使用

    一.简要阐述 ASP.NET Core引入了Options模式,使用类来表示相关的设置组.简单的来说,就是用强类型的类来表达配置项,这带来了很多好处.利用了系统的依赖注入,并且还可以利用配置系统.它使 ...

  2. Asp.Net Core 选项模式的三种注入方式

    前言 记录下最近在成都的面试题, 选项模式的热更新, 没答上来 正文 选项模式的依赖注入共有三种接口, 分别是 IOptions<>, IOptionsSnapshot<>, ...

  3. ASP.NET Core 选项模式源码学习Options Configure(一)

    前言 ASP.NET Core 后我们的配置变得更加轻量级了,在ASP.NET Core中,配置模型得到了显著的扩展和增强,应用程序配置可以存储在多环境变量配置中,appsettings.json用户 ...

  4. ASP.NET Core 选项模式源码学习Options IOptionsMonitor(三)

    前言 IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用.IOptionsMonitor用于检索选项并管理TOption实例的选项通知, IOpti ...

  5. ASP.NET Core 选项模式源码学习Options IOptions(二)

    前言 上一篇文章介绍IOptions的注册,本章我们继续往下看 IOptions IOptions是一个接口里面只有一个Values属性,该接口通过OptionsManager实现 public in ...

  6. (13)ASP.NET Core 中的选项模式(Options)

    1.前言 选项(Options)模式是对配置(Configuration)的功能的延伸.在12章(ASP.NET Core中的配置二)Configuration中有介绍过该功能(绑定到实体类.绑定至对 ...

  7. 基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传

    在基于SqlSugar的开发框架的服务层中处理文件上传的时候,我们一般有两种处理方式,一种是常规的把文件存储在本地文件系统中,一种是通过FTP方式存储到指定的FTP服务器上.这种处理应该由程序进行配置 ...

  8. 【ASP.NET Core】选项模式的相关接口

    在 .NET 中,配置与选项模式其实有联系的(这些功能现在不仅限于 ASP.NET Core,而是作为平台扩展来提供,在其他.NET 项目中都能用).配置一般从多个来源(上一篇水文中的例子,记得否?) ...

  9. ASP.NET Core 2.2 基础知识(七) 选项模式

    承接上一篇 配置, 选项模式是专门用类来表示相关配置的服务. 基本选项配置 新建一个选项类,该类必须是包含无参数的构造函数的非抽象类. public class MyOptions { public ...

随机推荐

  1. WALT(Window Assisted Load Tracking)学习

    QCOM平台使用WALT(Window Assisted Load Tracking)作为CPU load tracking的方法:相对地,ARM使用的是PELT(Per-Entity Load Tr ...

  2. (Java实现) 友好城市

    1263:[例9.7]友好城市 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 1867 通过数: 1032 [题目描述] Palmia国有一条横贯东西的大河,河有笔直的南北两岸, ...

  3. Java实现蓝桥杯模拟带九9的数的个数

    问题描述 在1至2019中,有多少个数的数位中包含数字9? 注意,有的数中的数位中包含多个9,这个数只算一次.例如,1999这个数包含数字9,在计算只是算一个数. 答案提交 这是一道结果填空的题,你只 ...

  4. 第七届蓝桥杯JavaA组国(决)赛部分真题

    解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.阶乘位数 阶乘位数 9的阶乘等于:362880 它的二进制表示为:1011000100110000000 这个数字共有19位. 请你计 ...

  5. java实现第七届蓝桥杯生日蜡烛

    生日蜡烛 生日蜡烛 某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛. 现在算起来,他一共吹熄了236根蜡烛. 请问,他从多少岁开始过生日party的? 请填写他开始过 ...

  6. 曹工说JDK源码(1)--ConcurrentHashMap,扩容前大家同在一个哈希桶,为啥扩容后,你去新数组的高位,我只能去低位?

    如何计算,一对key/value应该放在哪个哈希桶 大家都知道,hashmap底层是数组+链表(不讨论红黑树的情况),其中,这个数组,我们一般叫做哈希桶,大家如果去看jdk的源码,会发现里面有一些变量 ...

  7. Windows10 搭建 ElasticSearch 集群服务

    一.前言 集群的搭建需要多台机器,之前我使用 ubuntu 16.04 搭建过 hadoop 的单机模式和分布式模式,这个今后会写,今天先写一篇使用 < Windows10 搭建 Elastic ...

  8. Sublime 配置 Markdown,并实时预览

    准备: 找到菜单栏:Preferences → Package Control → Package Control:Install Package 需要安装的插件: [Markdown Editing ...

  9. Ubuntu18.04下MySQL8.0和Navicat15的安装与使用

    目录 一.MySQL8.0安装 二.Navicat安装并与MySQL连接 一.MySQL8.0安装 注意:若直接 sudo apt install mysql-server,你会发现安装后的版本是5. ...

  10. MySQL的LIKE模糊查询优化

    原文链接:https://www.cnblogs.com/whyat/p/10512797.html %xxx%这种方式对于数据量少的时候,我们倒可以随意用,但是数据量大的时候,我们就体验到了查询性能 ...