选项还提供验证配置数据的机制。 有关详细信息,请参阅 选项验证 部分。

本文介绍 ASP.NET Core 中的选项模式。 若要了解如何在控制台应用中使用选项模式,请参阅 .NET 中的选项模式

绑定分层配置

读取相关配置值的首选方法是使用 选项模式 。 例如,若要读取以下配置值,请执行以下操作:

  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"

创建以下 PositionOptions 类:

public class PositionOptions
    public const string Position = "Position";
    public string Title { get; set; } = String.Empty;
    public string Name { get; set; } = String.Empty;
  • 必须是非抽象的。
  • 绑定在配置中具有相应项的类型的公共读写属性。
  • 将其读写属性绑定到配置中的匹配条目。
  • 绑定字段。 在上面的代码中,Position 未绑定。 由于使用了 Position 字段,因此在将类绑定到配置提供程序时,不需要在应用中对字符串 "Position" 进行硬编码。
  • 下面的代码:

  • 调用 ConfigurationBinder.BindPositionOptions 类绑定到 Position 部分。
  • 显示 Position 配置数据。
  • public class Test22Model : PageModel
        private readonly IConfiguration Configuration;
        public Test22Model(IConfiguration configuration)
            Configuration = configuration;
        public ContentResult OnGet()
            var positionOptions = new PositionOptions();
            Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
            return Content($"Title: {positionOptions.Title} \n" +
                           $"Name: {positionOptions.Name}");
    

    在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

    ConfigurationBinder.Get<T> 绑定并返回指定的类型。 使用 ConfigurationBinder.Get<T> 可能比使用 ConfigurationBinder.Bind 更方便。 下面的代码演示如何将 ConfigurationBinder.Get<T>PositionOptions 类配合使用:

    public class Test21Model : PageModel
        private readonly IConfiguration Configuration;
        public PositionOptions? positionOptions { get; private set; }
        public Test21Model(IConfiguration configuration)
            Configuration = configuration;
        public ContentResult OnGet()
            positionOptions = Configuration.GetSection(PositionOptions.Position)
                                                         .Get<PositionOptions>();
            return Content($"Title: {positionOptions.Title} \n" +
                           $"Name: {positionOptions.Name}");
    

    在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

    Bind 还允许抽象类的缩进。 请看以下使用抽象类 SomethingWithAName 的代码:

    namespace ConfigSample.Options;
    public abstract class SomethingWithAName
        public abstract string? Name { get; set; }
    public class NameTitleOptions(int age) : SomethingWithAName
        public const string NameTitle = "NameTitle";
        public override string? Name { get; set; }
        public string Title { get; set; } = string.Empty;
        public int Age { get; set; } = age;
    

    以下代码显示 NameTitleOptions 配置值:

    public class Test33Model : PageModel
        private readonly IConfiguration Configuration;
        public Test33Model(IConfiguration configuration)
            Configuration = configuration;
        public ContentResult OnGet()
            var nameTitleOptions = new NameTitleOptions(22);
            Configuration.GetSection(NameTitleOptions.NameTitle).Bind(nameTitleOptions);
            return Content($"Title: {nameTitleOptions.Title} \n" +
                           $"Name: {nameTitleOptions.Name}  \n" +
                           $"Age: {nameTitleOptions.Age}"
    

    Bind 的调用比对 Get<> 的调用严格:

  • Bind 允许抽象的缩进。
  • Get<> 必须创建实例本身。
  • 使用选项模式时的另一种方法是绑定 Position 部分,并将其添加到依赖关系注入服务容器。 在以下代码中,PositionOptions 已通过 Configure 被添加到了服务容器并已绑定到了配置:

    using ConfigSample.Options;
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddRazorPages();
    builder.Services.Configure<PositionOptions>(
        builder.Configuration.GetSection(PositionOptions.Position));
    var app = builder.Build();
    

    通过使用前面的代码,以下代码将读取位置选项:

    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}");
    

    在上面的代码中,不会读取在应用启动后对 JSON 配置文件所做的更改。 若要读取在应用启动后的更改,请使用 IOptionsSnapshot

    IOptions<TOptions>

  • 不支持
    • 在应用启动后读取配置数据。
    • 注册为单一实例且可以注入到任何服务生存期
    • IOptionsSnapshot<TOptions>

    • 在每次请求时应重新计算选项的方案中有用。 有关详细信息,请参阅使用 IOptionsSnapshot 读取已更新的数据
    • 注册为范围内,因此无法注入到单一实例服务。
    • 支持命名选项
    • IOptionsMonitor<TOptions>

    • 用于检索选项并管理 TOptions 实例的选项通知。
    • 注册为单一实例且可以注入到任何服务生存期
    • 可重载配置
    • 选择性选项失效 (IOptionsMonitorCache<TOptions>)
    • 后期配置方案允许在进行所有 IConfigureOptions<TOptions> 配置后设置或更改选项。

      IOptionsFactory<TOptions> 负责新建选项实例。 它具有单个 Create 方法。 默认实现采用所有已注册 IConfigureOptions<TOptions>IPostConfigureOptions<TOptions> 并首先运行所有配置,然后才进行后期配置。 它区分 IConfigureNamedOptions<TOptions>IConfigureOptions<TOptions> 且仅调用适当的接口。

      IOptionsMonitorCache<TOptions>IOptionsMonitor<TOptions> 用于缓存 TOptions 实例。 IOptionsMonitorCache<TOptions> 可使监视器中的选项实例无效,以便重新计算值 (TryRemove)。 可以通过 TryAdd 手动引入值。 在应按需重新创建所有命名实例时使用 Clear 方法。

      使用 IOptionsSnapshot 读取已更新的数据

      使用 IOptionsSnapshot<TOptions>

    • 针对请求生存期访问和缓存选项时,每个请求只能计算一次选项。
    • 可能会产生显著的性能损失,因为它是范围内服务,并按请求重新计算。 有关详细信息,请参阅此 GitHub 问题改进配置绑定的性能
    • 当使用支持读取已更新的配置值的配置提供程序时,将在应用启动后读取对配置所做的更改。
    • IOptionsMonitorIOptionsSnapshot 之间的区别在于:

    • IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。
    • IOptionsSnapshot 是一种范围服务,并在构造 IOptionsSnapshot<T> 对象时提供选项的快照。 选项快照旨在用于暂时性和有作用域的依赖项。
    • 以下代码使用 IOptionsSnapshot<TOptions>

      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}");
      

      以下代码注册 MyOptions 绑定的配置实例:

      using SampleApp.Models;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddRazorPages();
      builder.Services.Configure<MyOptions>(
          builder.Configuration.GetSection("MyOptions"));
      var app = builder.Build();
      

      在上面的代码中,读取在应用启动后对 JSON 配置文件所做的更改。

      IOptionsMonitor

      以下代码注册 MyOptions 绑定的配置实例。

      using SampleApp.Models;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddRazorPages();
      builder.Services.Configure<MyOptions>(
          builder.Configuration.GetSection("MyOptions"));
      var app = builder.Build();
      

      下面的示例使用 IOptionsMonitor<TOptions>

      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}");
      

      在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

      命名选项支持使用 IConfigureNamedOptions

      命名选项:

    • 当多个配置节绑定到同一属性时有用。
    • 区分大小写。
    • 请考虑以下 appsettings.json 文件:

      "TopItem": { "Month": { "Name": "Green Widget", "Model": "GW46" "Year": { "Name": "Orange Gadget", "Model": "OG35"

      下面的类用于每个节,而不是创建两个类来绑定 TopItem:MonthTopItem:Year

      public class TopItemSettings
          public const string Month = "Month";
          public const string Year = "Year";
          public string Name { get; set; } = string.Empty;
          public string Model { get; set; } = string.Empty;
      

      下面的代码将配置命名选项:

      using SampleApp.Models;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddRazorPages();
      builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
          builder.Configuration.GetSection("TopItem:Month"));
      builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
          builder.Configuration.GetSection("TopItem:Year"));
      var app = builder.Build();
      

      下面的代码将显示命名选项:

      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);
          public ContentResult OnGet()
              return Content($"Month:Name {_monthTopItem.Name} \n" +
                             $"Month:Model {_monthTopItem.Model} \n\n" +
                             $"Year:Name {_yearTopItem.Name} \n" +
                             $"Year:Model {_yearTopItem.Model} \n"   );
      

      所有选项都是命名实例。 IConfigureOptions<TOptions> 实例将被视为面向 Options.DefaultName 实例,即 string.EmptyIConfigureNamedOptions<TOptions> 还可实现 IConfigureOptions<TOptions>IOptionsFactory<TOptions> 的默认实现具有适当地使用每个实例的逻辑。 null 命名选项用于面向所有命名实例,而不是某一特定命名实例。 ConfigureAllPostConfigureAll 使用此约定。

      OptionsBuilder API

      OptionsBuilder<TOptions> 用于配置 TOptions 实例。 OptionsBuilder 简化了创建命名选项的过程,因为它只是初始 AddOptions<TOptions>(string optionsName) 调用的单个参数,而不会出现在所有后续调用中。 选项验证和接受服务依赖关系的 ConfigureOptions 重载仅可通过 OptionsBuilder 获得。

      OptionsBuilder选项验证部分中使用。

      有关添加自定义存储库的信息,请参阅使用 AddOptions 配置自定义存储库

      使用 DI 服务配置选项

      在配置选项时,可以通过以下两种方式通过依赖关系注入访问服务:

    • 将配置委托传递给 OptionsBuilder<TOptions> 上的 ConfigureOptionsBuilder<TOptions> 提供 Configure 的重载,该重载允许使用最多五个服务来配置选项:

      builder.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<TOptions>IConfigureNamedOptions<TOptions> 的类型,并将该类型注册为服务。

      建议将配置委托传递给 Configure,因为创建服务较复杂。 在调用 Configure 时,创建类型等效于框架执行的操作。 调用 Configure 会注册一个临时通用 IConfigureNamedOptions<TOptions>,它具有可接受指定的通用服务类型的构造函数。

      通过选项验证,可以验证选项值。

      请考虑以下 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; }
      

      下面的代码:

    • 调用 AddOptions 以获取绑定到 MyConfigOptions 类的 OptionsBuilder<TOptions>
    • 调用 ValidateDataAnnotations 以使用 DataAnnotations 启用验证。
    • using OptionsValidationSample.Configuration;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddControllersWithViews();
      builder.Services.AddOptions<MyConfigOptions>()
                  .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
                  .ValidateDataAnnotations();
      var app = builder.Build();
      

      ValidateDataAnnotations 扩展方法在 Microsoft.Extensions.Options.DataAnnotations NuGet 包中定义。 对于使用 Microsoft.NET.Sdk.Web SDK 的 Web 应用,通过共享框架隐式引用此包。

      下面的代码显示配置值或验证错误:

      public class HomeController : Controller
          private readonly ILogger<HomeController> _logger;
          private readonly IOptions<MyConfigOptions> _config;
          public HomeController(IOptions<MyConfigOptions> config,
                                ILogger<HomeController> logger)
              _config = config;
              _logger = logger;
                  var configValue = _config.Value;
              catch (OptionsValidationException ex)
                  foreach (var failure in ex.Failures)
                      _logger.LogError(failure);
          public ContentResult Index()
              string msg;
                  msg = $"Key1: {_config.Value.Key1} \n" +
                        $"Key2: {_config.Value.Key2} \n" +
                        $"Key3: {_config.Value.Key3}";
              catch (OptionsValidationException optValEx)
                  return Content(optValEx.Message);
              return Content(msg);
      

      下面的代码使用委托应用更复杂的验证规则:

      using OptionsValidationSample.Configuration;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddControllersWithViews();
      builder.Services.AddOptions<MyConfigOptions>()
                  .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
                  .ValidateDataAnnotations()
              .Validate(config =>
                  if (config.Key2 != 0)
                      return config.Key3 > config.Key2;
                  return true;
              }, "Key3 must be > than Key2.");   // Failure message.
      var app = builder.Build();
      

      IValidateOptions<TOptions>IValidatableObject

      下面的类实现了 IValidateOptions<TOptions>

      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 允许将验证代码移出 Program.cs 并将其移入类中。

      使用前面的代码,使用以下代码在 Program.cs 中启用验证:

      using Microsoft.Extensions.Options;
      using OptionsValidationSample.Configuration;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddControllersWithViews();
      builder.Services.Configure<MyConfigOptions>(builder.Configuration.GetSection(
                                              MyConfigOptions.MyConfig));
      builder.Services.AddSingleton<IValidateOptions
                                    <MyConfigOptions>, MyConfigValidation>();
      var app = builder.Build();
      

      选项验证也支持 IValidatableObject。 若要对类中的某个类执行类级验证,请执行以下操作:

    • 在类中实现 IValidatableObject 接口及其 Validate 方法。
    • Program.cs 中调用 ValidateDataAnnotations
    • ValidateOnStart

      选项验证将在第一次创建 IOptions<TOptions>IOptionsSnapshot<TOptions>IOptionsMonitor<TOptions> 实现时运行。 若要立即运行选项验证,请在应用启动时调用 Program.cs 中的 ValidateOnStart

      builder.Services.AddOptions<MyConfigOptions>()
          .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
          .ValidateDataAnnotations()
          .ValidateOnStart();
      

      选项后期配置

      使用 IPostConfigureOptions<TOptions> 设置后期配置。 进行所有 IConfigureOptions<TOptions> 配置后运行后期配置:

      using OptionsValidationSample.Configuration;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddControllersWithViews();
      builder.Services.AddOptions<MyConfigOptions>()
                      .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
      builder.Services.PostConfigure<MyConfigOptions>(myOptions =>
          myOptions.Key1 = "post_configured_key1_value";
      

      PostConfigure 可用于对命名选项进行后期配置:

      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddRazorPages();
      builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
          builder.Configuration.GetSection("TopItem:Month"));
      builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
          builder.Configuration.GetSection("TopItem:Year"));
      builder.Services.PostConfigure<TopItemSettings>("Month", myOptions =>
          myOptions.Name = "post_configured_name_value";
          myOptions.Model = "post_configured_model_value";
      var app = builder.Build();
      

      使用 PostConfigureAll 对所有配置实例进行后期配置:

      using OptionsValidationSample.Configuration;
      var builder = WebApplication.CreateBuilder(args);
      builder.Services.AddControllersWithViews();
      builder.Services.AddOptions<MyConfigOptions>()
                      .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
      builder.Services.PostConfigureAll<MyConfigOptions>(myOptions =>
          myOptions.Key1 = "post_configured_key1_value";
      

      访问 Program.cs 中的选项

      若要访问 Program.cs 中的 IOptions<TOptions>IOptionsMonitor<TOptions>,请在 WebApplication.Services 上调用 GetRequiredService

      var app = builder.Build();
      var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
          .CurrentValue.Option1;
      
    • 查看或下载示例代码如何下载
    • 作者:Kirk LarkinRick Anderson

      选项模式使用类来提供对相关设置组的强类型访问。 当配置设置由方案隔离到单独的类时,应用遵循两个重要软件工程原则:

    • 依赖于配置设置的类仅依赖于其使用的配置设置。
    • 分离关注点
      • 应用的不同部件的设置不彼此依赖或相互耦合。
      • 选项还提供验证配置数据的机制。 有关详细信息,请参阅选项验证部分。

        本文介绍 ASP.NET Core 中的选项模式。 若要了解如何在控制台应用中使用选项模式,请参阅 .NET 中的选项模式

        绑定分层配置

        读取相关配置值的首选方法是使用选项模式。 例如,若要读取以下配置值,请执行以下操作:

          "Position": {
            "Title": "Editor",
            "Name": "Joe Smith"
        

        创建以下 PositionOptions 类:

        public class PositionOptions
            public const string Position = "Position";
            public string Title { get; set; } = String.Empty;
            public string Name { get; set; } = String.Empty;
        
      • 必须是包含公共无参数构造函数的非抽象类。
      • 类型的所有公共读写属性都已绑定。
      • 字段不是绑定的。 在上面的代码中,Position 未绑定。 由于使用了 Position 字段,因此在将类绑定到配置提供程序时,不需要在应用中对字符串 "Position" 进行硬编码。
      • 下面的代码:

      • 调用 ConfigurationBinder.BindPositionOptions 类绑定到 Position 部分。
      • 显示 Position 配置数据。
      • public class Test22Model : PageModel
            private readonly IConfiguration Configuration;
            public Test22Model(IConfiguration configuration)
                Configuration = configuration;
            public ContentResult OnGet()
                var positionOptions = new PositionOptions();
                Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
                return Content($"Title: {positionOptions.Title} \n" +
                               $"Name: {positionOptions.Name}");
        

        在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

        ConfigurationBinder.Get<T> 绑定并返回指定的类型。 使用 ConfigurationBinder.Get<T> 可能比使用 ConfigurationBinder.Bind 更方便。 下面的代码演示如何将 ConfigurationBinder.Get<T>PositionOptions 类配合使用:

        public class Test21Model : PageModel
            private readonly IConfiguration Configuration;
            public PositionOptions? positionOptions { get; private set; }
            public Test21Model(IConfiguration configuration)
                Configuration = configuration;
            public ContentResult OnGet()
                positionOptions = Configuration.GetSection(PositionOptions.Position)
                                                             .Get<PositionOptions>();
                return Content($"Title: {positionOptions.Title} \n" +
                               $"Name: {positionOptions.Name}");
        

        在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

        使用选项模式时的另一种方法是绑定 Position 部分,并将其添加到依赖关系注入服务容器。 在以下代码中,PositionOptions 已通过 Configure 被添加到了服务容器并已绑定到了配置:

        using ConfigSample.Options;
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddRazorPages();
        builder.Services.Configure<PositionOptions>(
            builder.Configuration.GetSection(PositionOptions.Position));
        var app = builder.Build();
        

        通过使用前面的代码,以下代码将读取位置选项:

        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}");
        

        在上面的代码中,不会读取在应用启动后对 JSON 配置文件所做的更改。 若要读取在应用启动后的更改,请使用 IOptionsSnapshot

        IOptions<TOptions>

      • 不支持
        • 在应用启动后读取配置数据。
        • 注册为单一实例且可以注入到任何服务生存期
        • IOptionsSnapshot<TOptions>

        • 在每次请求时应重新计算选项的方案中有用。 有关详细信息,请参阅使用 IOptionsSnapshot 读取已更新的数据
        • 注册为范围内,因此无法注入到单一实例服务。
        • 支持命名选项
        • IOptionsMonitor<TOptions>

        • 用于检索选项并管理 TOptions 实例的选项通知。
        • 注册为单一实例且可以注入到任何服务生存期
        • 可重载配置
        • 选择性选项失效 (IOptionsMonitorCache<TOptions>)
        • 后期配置方案允许在进行所有 IConfigureOptions<TOptions> 配置后设置或更改选项。

          IOptionsFactory<TOptions> 负责新建选项实例。 它具有单个 Create 方法。 默认实现采用所有已注册 IConfigureOptions<TOptions>IPostConfigureOptions<TOptions> 并首先运行所有配置,然后才进行后期配置。 它区分 IConfigureNamedOptions<TOptions>IConfigureOptions<TOptions> 且仅调用适当的接口。

          IOptionsMonitorCache<TOptions>IOptionsMonitor<TOptions> 用于缓存 TOptions 实例。 IOptionsMonitorCache<TOptions> 可使监视器中的选项实例无效,以便重新计算值 (TryRemove)。 可以通过 TryAdd 手动引入值。 在应按需重新创建所有命名实例时使用 Clear 方法。

          使用 IOptionsSnapshot 读取已更新的数据

          使用 IOptionsSnapshot<TOptions>

        • 针对请求生存期访问和缓存选项时,每个请求只能计算一次选项。
        • 可能会产生显著的性能损失,因为它是范围内服务,并按请求重新计算。 有关详细信息,请参阅此 GitHub 问题改进配置绑定的性能
        • 当使用支持读取已更新的配置值的配置提供程序时,将在应用启动后读取对配置所做的更改。
        • IOptionsMonitorIOptionsSnapshot 之间的区别在于:

        • IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。
        • IOptionsSnapshot 是一种范围服务,并在构造 IOptionsSnapshot<T> 对象时提供选项的快照。 选项快照旨在用于暂时性和有作用域的依赖项。
        • 以下代码使用 IOptionsSnapshot<TOptions>

          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}");
          

          以下代码注册 MyOptions 绑定的配置实例:

          using SampleApp.Models;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddRazorPages();
          builder.Services.Configure<MyOptions>(
              builder.Configuration.GetSection("MyOptions"));
          var app = builder.Build();
          

          在上面的代码中,读取在应用启动后对 JSON 配置文件所做的更改。

          IOptionsMonitor

          以下代码注册 MyOptions 绑定的配置实例。

          using SampleApp.Models;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddRazorPages();
          builder.Services.Configure<MyOptions>(
              builder.Configuration.GetSection("MyOptions"));
          var app = builder.Build();
          

          下面的示例使用 IOptionsMonitor<TOptions>

          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}");
          

          在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

          命名选项支持使用 IConfigureNamedOptions

          命名选项:

        • 当多个配置节绑定到同一属性时有用。
        • 区分大小写。
        • 请考虑以下 appsettings.json 文件:

          "TopItem": { "Month": { "Name": "Green Widget", "Model": "GW46" "Year": { "Name": "Orange Gadget", "Model": "OG35"

          下面的类用于每个节,而不是创建两个类来绑定 TopItem:MonthTopItem:Year

          public class TopItemSettings
              public const string Month = "Month";
              public const string Year = "Year";
              public string Name { get; set; } = string.Empty;
              public string Model { get; set; } = string.Empty;
          

          下面的代码将配置命名选项:

          using SampleApp.Models;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddRazorPages();
          builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
              builder.Configuration.GetSection("TopItem:Month"));
          builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
              builder.Configuration.GetSection("TopItem:Year"));
          var app = builder.Build();
          

          下面的代码将显示命名选项:

          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);
              public ContentResult OnGet()
                  return Content($"Month:Name {_monthTopItem.Name} \n" +
                                 $"Month:Model {_monthTopItem.Model} \n\n" +
                                 $"Year:Name {_yearTopItem.Name} \n" +
                                 $"Year:Model {_yearTopItem.Model} \n"   );
          

          所有选项都是命名实例。 IConfigureOptions<TOptions> 实例将被视为面向 Options.DefaultName 实例,即 string.EmptyIConfigureNamedOptions<TOptions> 还可实现 IConfigureOptions<TOptions>IOptionsFactory<TOptions> 的默认实现具有适当地使用每个实例的逻辑。 null 命名选项用于面向所有命名实例,而不是某一特定命名实例。 ConfigureAllPostConfigureAll 使用此约定。

          OptionsBuilder API

          OptionsBuilder<TOptions> 用于配置 TOptions 实例。 OptionsBuilder 简化了创建命名选项的过程,因为它只是初始 AddOptions<TOptions>(string optionsName) 调用的单个参数,而不会出现在所有后续调用中。 选项验证和接受服务依赖关系的 ConfigureOptions 重载仅可通过 OptionsBuilder 获得。

          OptionsBuilder选项验证部分中使用。

          有关添加自定义存储库的信息,请参阅使用 AddOptions 配置自定义存储库

          使用 DI 服务配置选项

          在配置选项时,可以通过以下两种方式通过依赖关系注入访问服务:

        • 将配置委托传递给 OptionsBuilder<TOptions> 上的 ConfigureOptionsBuilder<TOptions> 提供 Configure 的重载,该重载允许使用最多五个服务来配置选项:

          builder.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<TOptions>IConfigureNamedOptions<TOptions> 的类型,并将该类型注册为服务。

          建议将配置委托传递给 Configure,因为创建服务较复杂。 在调用 Configure 时,创建类型等效于框架执行的操作。 调用 Configure 会注册一个临时通用 IConfigureNamedOptions<TOptions>,它具有可接受指定的通用服务类型的构造函数。

          通过选项验证,可以验证选项值。

          请考虑以下 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; }
          

          下面的代码:

        • 调用 AddOptions 以获取绑定到 MyConfigOptions 类的 OptionsBuilder<TOptions>
        • 调用 ValidateDataAnnotations 以使用 DataAnnotations 启用验证。
        • using OptionsValidationSample.Configuration;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddControllersWithViews();
          builder.Services.AddOptions<MyConfigOptions>()
                      .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
                      .ValidateDataAnnotations();
          var app = builder.Build();
          

          ValidateDataAnnotations 扩展方法在 Microsoft.Extensions.Options.DataAnnotations NuGet 包中定义。 对于使用 Microsoft.NET.Sdk.Web SDK 的 Web 应用,通过共享框架隐式引用此包。

          下面的代码显示配置值或验证错误:

          public class HomeController : Controller
              private readonly ILogger<HomeController> _logger;
              private readonly IOptions<MyConfigOptions> _config;
              public HomeController(IOptions<MyConfigOptions> config,
                                    ILogger<HomeController> logger)
                  _config = config;
                  _logger = logger;
                      var configValue = _config.Value;
                  catch (OptionsValidationException ex)
                      foreach (var failure in ex.Failures)
                          _logger.LogError(failure);
              public ContentResult Index()
                  string msg;
                      msg = $"Key1: {_config.Value.Key1} \n" +
                            $"Key2: {_config.Value.Key2} \n" +
                            $"Key3: {_config.Value.Key3}";
                  catch (OptionsValidationException optValEx)
                      return Content(optValEx.Message);
                  return Content(msg);
          

          下面的代码使用委托应用更复杂的验证规则:

          using OptionsValidationSample.Configuration;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddControllersWithViews();
          builder.Services.AddOptions<MyConfigOptions>()
                      .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
                      .ValidateDataAnnotations()
                  .Validate(config =>
                      if (config.Key2 != 0)
                          return config.Key3 > config.Key2;
                      return true;
                  }, "Key3 must be > than Key2.");   // Failure message.
          var app = builder.Build();
          

          IValidateOptions<TOptions>IValidatableObject

          下面的类实现了 IValidateOptions<TOptions>

          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 允许将验证代码移出 Program.cs 并将其移入类中。

          使用前面的代码,使用以下代码在 Program.cs 中启用验证:

          using Microsoft.Extensions.Options;
          using OptionsValidationSample.Configuration;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddControllersWithViews();
          builder.Services.Configure<MyConfigOptions>(builder.Configuration.GetSection(
                                                  MyConfigOptions.MyConfig));
          builder.Services.AddSingleton<IValidateOptions
                                        <MyConfigOptions>, MyConfigValidation>();
          var app = builder.Build();
          

          选项验证也支持 IValidatableObject。 若要对类中的某个类执行类级验证,请执行以下操作:

        • 在类中实现 IValidatableObject 接口及其 Validate 方法。
        • Program.cs 中调用 ValidateDataAnnotations
        • ValidateOnStart

          选项验证将在第一次创建 IOptions<TOptions>IOptionsSnapshot<TOptions>IOptionsMonitor<TOptions> 实现时运行。 若要立即运行选项验证,请在应用启动时调用 Program.cs 中的 ValidateOnStart

          builder.Services.AddOptions<MyConfigOptions>()
              .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
              .ValidateDataAnnotations()
              .ValidateOnStart();
          

          选项后期配置

          使用 IPostConfigureOptions<TOptions> 设置后期配置。 进行所有 IConfigureOptions<TOptions> 配置后运行后期配置:

          using OptionsValidationSample.Configuration;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddControllersWithViews();
          builder.Services.AddOptions<MyConfigOptions>()
                          .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
          builder.Services.PostConfigure<MyConfigOptions>(myOptions =>
              myOptions.Key1 = "post_configured_key1_value";
          

          PostConfigure 可用于对命名选项进行后期配置:

          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddRazorPages();
          builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
              builder.Configuration.GetSection("TopItem:Month"));
          builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
              builder.Configuration.GetSection("TopItem:Year"));
          builder.Services.PostConfigure<TopItemSettings>("Month", myOptions =>
              myOptions.Name = "post_configured_name_value";
              myOptions.Model = "post_configured_model_value";
          var app = builder.Build();
          

          使用 PostConfigureAll 对所有配置实例进行后期配置:

          using OptionsValidationSample.Configuration;
          var builder = WebApplication.CreateBuilder(args);
          builder.Services.AddControllersWithViews();
          builder.Services.AddOptions<MyConfigOptions>()
                          .Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
          builder.Services.PostConfigureAll<MyConfigOptions>(myOptions =>
              myOptions.Key1 = "post_configured_key1_value";
          

          访问 Program.cs 中的选项

          若要访问 Program.cs 中的 IOptions<TOptions>IOptionsMonitor<TOptions>,请在 WebApplication.Services 上调用 GetRequiredService

          var app = builder.Build();
          var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
              .CurrentValue.Option1;
          
        • 查看或下载示例代码如何下载
        • 作者:Kirk LarkinRick Anderson

          选项模式使用类来提供对相关设置组的强类型访问。 当配置设置由方案隔离到单独的类时,应用遵循两个重要软件工程原则:

        • 依赖于配置设置的类仅依赖于其使用的配置设置。
        • 分离关注点
          • 应用的不同部件的设置不彼此依赖或相互耦合。
          • 选项还提供验证配置数据的机制。 有关详细信息,请参阅选项验证部分。

            本主题介绍 ASP.NET Core 中的选项模式。 若要了解如何在控制台应用中使用选项模式,请参阅 .NET 中的选项模式

            查看或下载示例代码如何下载

            绑定分层配置

            读取相关配置值的首选方法是使用选项模式。 例如,若要读取以下配置值,请执行以下操作:

              "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.BindPositionOptions 类绑定到 Position 部分。
          • 显示 Position 配置数据。
          • public class Test22Model : PageModel
                private readonly IConfiguration Configuration;
                public Test22Model(IConfiguration configuration)
                    Configuration = configuration;
                public ContentResult OnGet()
                    var positionOptions = new PositionOptions();
                    Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
                    return Content($"Title: {positionOptions.Title} \n" +
                                   $"Name: {positionOptions.Name}");
            

            在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

            ConfigurationBinder.Get<T> 绑定并返回指定的类型。 使用 ConfigurationBinder.Get<T> 可能比使用 ConfigurationBinder.Bind 更方便。 下面的代码演示如何将 ConfigurationBinder.Get<T>PositionOptions 类配合使用:

            public class Test21Model : PageModel
                private readonly IConfiguration Configuration;
                public PositionOptions positionOptions { get; private set; }
                public Test21Model(IConfiguration configuration)
                    Configuration = configuration;
                public ContentResult OnGet()
                    positionOptions = Configuration.GetSection(PositionOptions.Position)
                                                                 .Get<PositionOptions>();
                    return Content($"Title: {positionOptions.Title} \n" +
                                   $"Name: {positionOptions.Name}");
            

            在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

            使用选项模式时的另一种方法是绑定 Position 部分,并将其添加到依赖关系注入服务容器。 在以下代码中,PositionOptions 已通过 Configure 被添加到了服务容器并已绑定到了配置:

            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}");
            

            在上面的代码中,不会读取在应用启动后对 JSON 配置文件所做的更改。 若要读取在应用启动后的更改,请使用 IOptionsSnapshot

            IOptions<TOptions>

          • 不支持
            • 在应用启动后读取配置数据。
            • 注册为单一实例且可以注入到任何服务生存期
            • IOptionsSnapshot<TOptions>

            • 在每次请求时应重新计算选项的方案中有用。 有关详细信息,请参阅使用 IOptionsSnapshot 读取已更新的数据
            • 注册为范围内,因此无法注入到单一实例服务。
            • 支持命名选项
            • IOptionsMonitor<TOptions>

            • 用于检索选项并管理 TOptions 实例的选项通知。
            • 注册为单一实例且可以注入到任何服务生存期
            • 可重载配置
            • 选择性选项失效 (IOptionsMonitorCache<TOptions>)
            • 后期配置方案允许在进行所有 IConfigureOptions<TOptions> 配置后设置或更改选项。

              IOptionsFactory<TOptions> 负责新建选项实例。 它具有单个 Create 方法。 默认实现采用所有已注册 IConfigureOptions<TOptions>IPostConfigureOptions<TOptions> 并首先运行所有配置,然后才进行后期配置。 它区分 IConfigureNamedOptions<TOptions>IConfigureOptions<TOptions> 且仅调用适当的接口。

              IOptionsMonitorCache<TOptions>IOptionsMonitor<TOptions> 用于缓存 TOptions 实例。 IOptionsMonitorCache<TOptions> 可使监视器中的选项实例无效,以便重新计算值 (TryRemove)。 可以通过 TryAdd 手动引入值。 在应按需重新创建所有命名实例时使用 Clear 方法。

              使用 IOptionsSnapshot 读取已更新的数据

              通过使用 IOptionsSnapshot<TOptions>,针对请求生存期访问和缓存选项时,每个请求都会计算一次选项。 当使用支持读取已更新的配置值的配置提供程序时,将在应用启动后读取对配置所做的更改。

              IOptionsMonitorIOptionsSnapshot 之间的区别在于:

            • IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。
            • IOptionsSnapshot 是一种范围服务,并在构造 IOptionsSnapshot<T> 对象时提供选项的快照。 选项快照旨在用于暂时性和有作用域的依赖项。
            • 以下代码使用 IOptionsSnapshot<TOptions>

              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}");
              

              以下代码注册 MyOptions 绑定的配置实例:

              public void ConfigureServices(IServiceCollection services)
                  services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
                  services.AddRazorPages();
              

              在上面的代码中,读取在应用启动后对 JSON 配置文件所做的更改。

              IOptionsMonitor

              以下代码注册 MyOptions 绑定的配置实例。

              public void ConfigureServices(IServiceCollection services)
                  services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
                  services.AddRazorPages();
              

              下面的示例使用 IOptionsMonitor<TOptions>

              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}");
              

              在上面的代码中,默认读取在应用启动后对 JSON 配置文件所做的更改。

              命名选项支持使用 IConfigureNamedOptions

              命名选项:

            • 当多个配置节绑定到同一属性时有用。
            • 区分大小写。
            • 请考虑以下 appsettings.json 文件:

              "TopItem": { "Month": { "Name": "Green Widget", "Model": "GW46" "Year": { "Name": "Orange Gadget", "Model": "OG35"

              下面的类用于每个节,而不是创建两个类来绑定 TopItem:MonthTopItem: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);
                  public ContentResult OnGet()
                      return Content($"Month:Name {_monthTopItem.Name} \n" +
                                     $"Month:Model {_monthTopItem.Model} \n\n" +
                                     $"Year:Name {_yearTopItem.Name} \n" +
                                     $"Year:Model {_yearTopItem.Model} \n"   );
              

              所有选项都是命名实例。 IConfigureOptions<TOptions> 实例将被视为面向 Options.DefaultName 实例,即 string.EmptyIConfigureNamedOptions<TOptions> 还可实现 IConfigureOptions<TOptions>IOptionsFactory<TOptions> 的默认实现具有适当地使用每个实例的逻辑。 null 命名选项用于面向所有命名实例,而不是某一特定命名实例。 ConfigureAllPostConfigureAll 使用此约定。

              OptionsBuilder API

              OptionsBuilder<TOptions> 用于配置 TOptions 实例。 OptionsBuilder 简化了创建命名选项的过程,因为它只是初始 AddOptions<TOptions>(string optionsName) 调用的单个参数,而不会出现在所有后续调用中。 选项验证和接受服务依赖关系的 ConfigureOptions 重载仅可通过 OptionsBuilder 获得。

              OptionsBuilder选项验证部分中使用。

              有关添加自定义存储库的信息,请参阅使用 AddOptions 配置自定义存储库

              使用 DI 服务配置选项

              在配置选项时,可以通过以下两种方式通过依赖关系注入访问服务:

            • 将配置委托传递给 OptionsBuilder<TOptions> 上的 ConfigureOptionsBuilder<TOptions> 提供 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<TOptions>IConfigureNamedOptions<TOptions> 的类型,并将该类型注册为服务。

              建议将配置委托传递给 Configure,因为创建服务较复杂。 在调用 Configure 时,创建类型等效于框架执行的操作。 调用 Configure 会注册一个临时通用 IConfigureNamedOptions<TOptions>,它具有可接受指定的通用服务类型的构造函数。

              通过选项验证,可以验证选项值。

              请考虑以下 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; }
              

              下面的代码:

            • 调用 AddOptions 以获取绑定到 MyConfigOptions 类的 OptionsBuilder<TOptions>
            • 调用 ValidateDataAnnotations 以使用 DataAnnotations 启用验证。
            • public class Startup
                  public Startup(IConfiguration configuration)
                      Configuration = configuration;
                  public IConfiguration Configuration { get; }
                  public void ConfigureServices(IServiceCollection services)
                      services.AddOptions<MyConfigOptions>()
                          .Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
                          .ValidateDataAnnotations();
                      services.AddControllersWithViews();
              

              ValidateDataAnnotations 扩展方法在 Microsoft.Extensions.Options.DataAnnotations NuGet 包中定义。 对于使用 Microsoft.NET.Sdk.Web SDK 的 Web 应用,通过共享框架隐式引用此包。

              下面的代码显示配置值或验证错误:

              public class HomeController : Controller
                  private readonly ILogger<HomeController> _logger;
                  private readonly IOptions<MyConfigOptions> _config;
                  public HomeController(IOptions<MyConfigOptions> config,
                                        ILogger<HomeController> logger)
                      _config = config;
                      _logger = logger;
                          var configValue = _config.Value;
                      catch (OptionsValidationException ex)
                          foreach (var failure in ex.Failures)
                              _logger.LogError(failure);
                  public ContentResult Index()
                      string msg;
                           msg = $"Key1: {_config.Value.Key1} \n" +
                                 $"Key2: {_config.Value.Key2} \n" +
                                 $"Key3: {_config.Value.Key3}";
                      catch (OptionsValidationException optValEx)
                          return Content(optValEx.Message);
                      return Content(msg);
              

              下面的代码使用委托应用更复杂的验证规则:

              public void ConfigureServices(IServiceCollection services)
                  services.AddOptions<MyConfigOptions>()
                      .Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
                      .ValidateDataAnnotations()
                      .Validate(config =>
                          if (config.Key2 != 0)
                              return config.Key3 > config.Key2;
                          return true;
                      }, "Key3 must be > than Key2.");   // Failure message.
                  services.AddControllersWithViews();
              

              用于复杂验证的 IValidateOptions

              下面的类实现了 IValidateOptions<TOptions>

              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<TOptions> 设置后期配置。 进行所有 IConfigureOptions<TOptions> 配置后运行后期配置:

              services.PostConfigure<MyOptions>(myOptions =>
                  myOptions.Option1 = "post_configured_option1_value";
              

              PostConfigure 可用于对命名选项进行后期配置:

              services.PostConfigure<MyOptions>("named_options_1", myOptions =>
                  myOptions.Option1 = "post_configured_option1_value";
              

              使用 PostConfigureAll 对所有配置实例进行后期配置:

              services.PostConfigureAll<MyOptions>(myOptions =>
                  myOptions.Option1 = "post_configured_option1_value";
              

              在启动期间访问选项

              IOptions<TOptions>IOptionsMonitor<TOptions> 可用于 Startup.Configure 中,因为在 Configure 方法执行之前已生成服务。

              public void Configure(IApplicationBuilder app, 
                  IOptionsMonitor<MyOptions> optionsAccessor)
                  var option1 = optionsAccessor.CurrentValue.Option1;
              

              不使用 Startup.ConfigureServices 中的 IOptions<TOptions>IOptionsMonitor<TOptions>。 由于服务注册的顺序,可能存在不一致的选项状态。

              ConfigurationExtensions NuGet 包

              Microsoft.Extensions.Options.ConfigurationExtensions 包已在 ASP.NET Core 应用中隐式引用。

  •