如何使用.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));

  • 创建实现 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】的详细内容,更多关于.NET Core 选项模式【Options】的资料请关注我们其它相关文章!

(0)

相关推荐

  • 解读ASP.NET 5 & MVC6系列教程(15):MvcOptions配置

    程序模型处理 IApplicationModelConvention 在MvcOptions的实例对象上,有一个ApplicationModelConventions属性(类型是:List<IApplicationModelConvention>),该属性IApplicationModelConvention类型的接口集合,用于处理应用模型ApplicationModel,该集合是在MVC程序启动的时候进行调用,所以在调用之前,我们可以对其进行修改或更新,比如,我们可以针对所有的Control

  • 如何使用.NET Core 选项模式【Options】

    ASP.NET Core引入了Options模式,使用类来表示相关的设置组.简单的来说,就是用强类型的类来表达配置项,这带来了很多好处.利用了系统的依赖注入,并且还可以利用配置系统.它使我们可以采用依赖注入的方法直接使用绑定的一个POCO对象,这个POCO对象就叫做Options对象.也可以叫做配置对象. 以下大多内容来自官方文档,我只是个翻译官或者叫搬运工吧! 引入Options扩展包 PM>Package-install Microsoft.Extensions.Options 绑定分层配置

  • ASP.NET Core中的Options选项模式

    1.前言 选项(Options)模式是对配置(Configuration)的功能的延伸.在12章(ASP.NET Core中的配置二)Configuration中有介绍过该功能(绑定到实体类.绑定至对象图.将数组绑定至类)而选项模式又有个选项类(TOptions),该选项类作用是指:把选项类中的属性与配置来源中的键关联起来.举个例,假设json文件有个Option1键,选项类中也有个叫Option1的属性名,经过选项配置,这样就能把json中的键的值映射到选项类属性值中.也可以理解在项目应用中,

  • GO 函数式选项模式(Functional Options Pattern)

    Golang 开发者遇到的许多问题之一是尝试将一个函数的参数设置为可选. 这是一个非常常见的用例, 有些对象应该使用一些基本的默认设置来开箱即用, 并且你偶尔可能需要提供一些更详细的配置. 在很多语言中这很容易; 在 C 族语言中, 可以使用不同数量的参数提供相同函数的多个版本; 在像 PHP 这样的语言中, 可以给参数一个默认值,并在调用方法时忽略它们. 但是在 Golang 中, 这两种方式你哪个也用不了. 那么你如何创建一个函数, 用户可以指定一些额外的配置? 有很多可能的方法可以做到这一

  • 详解Golang函数式选项(Functional Options)模式

    概览 最近阅读源码的时候看到一段不错的代码,但是当时却不是非常理解为什么这么写. 我们先来看一下源代码: type User struct { ID string Name string Age int Email string Phone string Gender string } type Option func(*User) func WithAge(age int) Option { return func(u *User) { u.Age = age } } func WithEma

  • ASP.NET Core选项接口介绍

    首先要了解 ASP.NET Core 中的配置,请点击这里了解:https://www.jb51.net/article/238451.htm 1,选项接口 ASP.NET Core 中的选项接口,一共有三个,分别是: IOptions<TOptions> IOptionsSnapshot<TOptions> IOptionsMonitor<TOptions> 这三种方式都可以获取到配置,区别在于生命周期和文件监控等. 2,注入配置与IOptions 首先我们创建一个

  • 解析ASP.NET Core中Options模式的使用及其源码

    目录 1.Options模式的用法 2.Options模式源码解析 3.最佳实践 本章将和大家分享ASP.NET Core中Options模式的使用及其源码解析. 在ASP.NET Core中引入了Options这一使用配置方式,其主要是为了解决依赖注入时需要传递指定数据问题(不是自行获取,而是能集中配置).通常来讲我们会把所需要的配置通过IConfiguration对象配置成一个普通的类,并且习惯上我们会把这个类的名字后缀加上Options.所以我们在使用某一个中间件或者使用第三方类库时,经常

  • 详解IIS在ASP.NET Core下的两种部署模式

    目录 一.ASP.NET CORE Core Module 二. In-Process部署模式 三.Out-of-Process部署模式 四.<aspnetcore>配置 KestrelServer最大的优势体现在它的跨平台的能力,如果ASP.NET CORE应用只需要部署在Windows环境下,IIS也是不错的选择.ASP.NET CORE应用针对IIS具有两种部署模式,它们都依赖于一个IIS针对ASP.NET CORE Core的扩展模块.本文提供的示例演示已经同步到<ASP.NET

  • ASP.NET Core MVC 依赖注入View与Controller

    目录 一.ASP.NET Core MVC 之依赖注入 View 1.填充查找数据 2.重写服务 二. ASP.NET Core MVC 之依赖注入 Controller 1.构造函数注入 2.使用 FromServices 操作注入 3.在控制器中访问设置 一.ASP.NET Core MVC 之依赖注入 View ASP.NET Core 支持在试图中使用依赖注入.这将有助于提供视图专用的服务,比如本地化或者仅用于填充视图元素的数据.应尽量保持控制器和视图之间的关注点分离.视图所显示的大部分

  • ASP.NET Core MVC控制器请求依赖注入

    ASP.NET Core MVC 控制器应通过构造函数明确地请求它们地依赖关系,在某些情况下,单个控制器地操作可能需要一个服务,在控制器级别上的请求可能没有意义.在这种情况下,也可以将服务作为  Action 的参数. 依赖注入是一种如 Dependency Inversion Principle 所示的技术,允许应用程序松散耦合的模块组成. 1.构造函数注入 ASP.NET Core 内置的基于构造函数的依赖注入支持扩展到 MVC 控制器.通过只添加一个服务类型作为构造函数参数到控制器中,AS

  • Mootools 1.2教程 Fx.Morph、Fx选项和Fx事件

    我们将学习如何使用Fx.Morph(它从本质上可以让你同时渐变多个样式表属性),然后我们再检查一下应用到Fx.Tween和Fx.Morph的一些Fx选项,最后我们将看看如何使用Fx事件,譬如"onComplete"和"onStart".通过这些选项和事件,我们可以获得更好的控制权来控制形变动画. Fx.Morph 创建一个新的Fx.Morph 初始化一个新的形变和创建一个新的渐变很类似,除了你要指定多个样式属性以外. 参考代码: 复制代码 代码如下: // 首先,把

随机推荐