ASP.NET Core 中的选项模式Options pattern in ASP.NET Core

作者:Kirk LarkinRick AndersonBy Kirk Larkin and Rick Anderson.

选项模式使用类来提供对相关设置组的强类型访问。The options pattern uses classes to provide strongly typed access to groups of related settings. 配置设置由方案隔离到单独的类时,应用遵循两个重要软件工程原则:When configuration settings are isolated by scenario into separate classes, the app adheres to two important software engineering principles:

选项还提供验证配置数据的机制。Options also provide a mechanism to validate configuration data. 有关详细信息,请参阅选项验证部分。For more information, see the Options validation section.

查看或下载示例代码如何下载View or download sample code (how to download)

绑定分层配置Bind hierarchical configuration

读取相关配置值的首选方法是使用选项模式The preferred way to read related configuration values is using the options pattern. 例如,若要读取以下配置值,请执行以下操作:For example, to read the following configuration values:

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

创建以下 PositionOptions 类:Create the following PositionOptions class:

public class PositionOptions
{
    public const string Position = "Position";

    public string Title { get; set; }
    public string Name { get; set; }
}

选项类:An options class:

  • 必须是包含公共无参数构造函数的非抽象类。Must be non-abstract with a public parameterless constructor.
  • 类型的所有公共读写属性都已绑定。All public read-write properties of the type are bound.
  • 不会绑定字段。Fields are not bound. 在上面的代码中,Position 未绑定。In the preceding code, Position is not bound. 由于使用了 Position 属性,因此在将类绑定到配置提供程序时,不需要在应用中对字符串 "Position" 进行硬编码。The Position property is used so the string "Position" doesn't need to be hard coded in the app when binding the class to a configuration provider.

下面的代码:The following code:

  • 调用 ConfigurationBinder.BindPositionOptions 类绑定到 Position 部分。Calls ConfigurationBinder.Bind to bind the PositionOptions class to the Position section.
  • 显示 Position 配置数据。Displays the Position configuration data.
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 配置文件所做的更改。In the preceding code, by default, changes to the JSON configuration file after the app has started are read.

ConfigurationBinder.Get<T> 绑定并返回指定的类型。ConfigurationBinder.Get<T> binds and returns the specified type. 使用 ConfigurationBinder.Get<T> 可能比使用 ConfigurationBinder.Bind 更方便。ConfigurationBinder.Get<T> may be more convenient than using ConfigurationBinder.Bind. 下面的代码演示如何将 ConfigurationBinder.Get<T>PositionOptions 类配合使用:The following code shows how to use ConfigurationBinder.Get<T> with the PositionOptions class:

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 配置文件所做的更改。In the preceding code, by default, changes to the JSON configuration file after the app has started are read.

使用选项模式时的替代方法是绑定 Position 部分并将它添加到依赖项注入服务容器An alternative approach when using the options pattern is to bind the Position section and add it to the dependency injection service container. 在以下代码中,PositionOptions 已通过 Configure 被添加到了服务容器并已绑定到了配置:In the following code, PositionOptions is added to the service container with Configure and bound to configuration:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(Configuration.GetSection(
                                        PositionOptions.Position));
    services.AddRazorPages();
}

通过使用前面的代码,以下代码将读取位置选项:Using the preceding code, the following code reads the position options:

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 配置文件所做的更改。In the preceding code, changes to the JSON configuration file after the app has started are not read. 若要读取在应用启动后的更改,请使用 IOptionsSnapshotTo read changes after the app has started, use IOptionsSnapshot.

选项接口Options interfaces

IOptions<TOptions>IOptions<TOptions>:

IOptionsSnapshot<TOptions>IOptionsSnapshot<TOptions>:

IOptionsMonitor<TOptions>IOptionsMonitor<TOptions>:

后期配置方案允许在进行所有 IConfigureOptions<TOptions> 配置后设置或更改选项。Post-configuration scenarios enable setting or changing options after all IConfigureOptions<TOptions> configuration occurs.

IOptionsFactory<TOptions> 负责新建选项实例。IOptionsFactory<TOptions> is responsible for creating new options instances. 它具有单个 Create 方法。It has a single Create method. 默认实现采用所有已注册 IConfigureOptions<TOptions>IPostConfigureOptions<TOptions> 并首先运行所有配置,然后才进行后期配置。The default implementation takes all registered IConfigureOptions<TOptions> and IPostConfigureOptions<TOptions> and runs all the configurations first, followed by the post-configuration. 它区分 IConfigureNamedOptions<TOptions>IConfigureOptions<TOptions> 且仅调用适当的接口。It distinguishes between IConfigureNamedOptions<TOptions> and IConfigureOptions<TOptions> and only calls the appropriate interface.

IOptionsMonitorCache<TOptions>IOptionsMonitor<TOptions> 用于缓存 TOptions 实例。IOptionsMonitorCache<TOptions> is used by IOptionsMonitor<TOptions> to cache TOptions instances. IOptionsMonitorCache<TOptions> 可使监视器中的选项实例无效,以便重新计算值 (TryRemove)。The IOptionsMonitorCache<TOptions> invalidates options instances in the monitor so that the value is recomputed (TryRemove). 可以通过 TryAdd 手动引入值。Values can be manually introduced with TryAdd. 在应按需重新创建所有命名实例时使用 Clear 方法。The Clear method is used when all named instances should be recreated on demand.

使用 IOptionsSnapshot 读取已更新的数据Use IOptionsSnapshot to read updated data

通过使用 IOptionsSnapshot<TOptions>,针对请求生存期访问和缓存选项时,每个请求都会计算一次选项。Using IOptionsSnapshot<TOptions>, options are computed once per request when accessed and cached for the lifetime of the request. 当使用支持读取已更新的配置值的配置提供程序时,将在应用启动后读取对配置所做的更改。Changes to the configuration are read after the app starts when using configuration providers that support reading updated configuration values.

IOptionsMonitorIOptionsSnapshot 之间的区别在于:The difference between IOptionsMonitor and IOptionsSnapshot is that:

  • IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。IOptionsMonitor is a singleton service that retrieves current option values at any time, which is especially useful in singleton dependencies.
  • IOptionsSnapshot 是一种作用域服务,并在构造 IOptionsSnapshot<T> 对象时提供选项的快照。IOptionsSnapshot is a scoped service and provides a snapshot of the options at the time the IOptionsSnapshot<T> object is constructed. 选项快照旨在用于暂时性和有作用域的依赖项。Options snapshots are designed for use with transient and scoped dependencies.

以下代码使用 IOptionsSnapshot<TOptions>The following code uses 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 绑定的配置实例:The following code registers a configuration instance which MyOptions binds against:

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

    services.AddRazorPages();
}

在上面的代码中,已读取在应用启动后对 JSON 配置文件所做的更改。In the preceding code, changes to the JSON configuration file after the app has started are read.

IOptionsMonitorIOptionsMonitor

以下代码注册 MyOptions 绑定的配置实例。The following code registers a configuration instance which MyOptions binds against.

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

    services.AddRazorPages();
}

下面的示例使用 IOptionsMonitor<TOptions>The following example uses 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 配置文件所做的更改。In the preceding code, by default, changes to the JSON configuration file after the app has started are read.

命名选项支持使用 IConfigureNamedOptionsNamed options support using IConfigureNamedOptions

命名选项:Named options:

  • 当多个配置节绑定到同一属性时有用。Are useful when multiple configuration sections bind to the same properties.
  • 区分大小写。Are case sensitive.

请考虑使用以下 appsettings.json 文件:Consider the following appsettings.json file:

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

下面的类用于每个节,而不是创建两个类来绑定 TopItem:MonthTopItem:YearRather than creating two classes to bind TopItem:Month and TopItem:Year, the following class is used for each section:

public class TopItemSettings
{
    public const string Month = "Month";
    public const string Year = "Year";

    public string Name { get; set; }
    public string Model { get; set; }
}

下面的代码将配置命名选项:The following code configures the named options:

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

下面的代码将显示命名选项:The following code displays the named options:

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

所有选项都是命名实例。All options are named instances. IConfigureOptions<TOptions> 实例将被视为面向 Options.DefaultName 实例,即 string.EmptyIConfigureOptions<TOptions> instances are treated as targeting the Options.DefaultName instance, which is string.Empty. IConfigureNamedOptions<TOptions> 还可实现 IConfigureOptions<TOptions>IConfigureNamedOptions<TOptions> also implements IConfigureOptions<TOptions>. IOptionsFactory<TOptions> 的默认实现具有适当地使用每个实例的逻辑。The default implementation of the IOptionsFactory<TOptions> has logic to use each appropriately. null 命名选项用于面向所有命名实例,而不是某一特定命名实例。The null named option is used to target all of the named instances instead of a specific named instance. ConfigureAllPostConfigureAll 使用此约定。ConfigureAll and PostConfigureAll use this convention.

OptionsBuilder APIOptionsBuilder API

OptionsBuilder<TOptions> 用于配置 TOptions 实例。OptionsBuilder<TOptions> is used to configure TOptions instances. OptionsBuilder 简化了创建命名选项的过程,因为它只是初始 AddOptions<TOptions>(string optionsName) 调用的单个参数,而不会出现在所有后续调用中。OptionsBuilder streamlines creating named options as it's only a single parameter to the initial AddOptions<TOptions>(string optionsName) call instead of appearing in all of the subsequent calls. 选项验证和接受服务依赖关系的 ConfigureOptions 重载仅可通过 OptionsBuilder 获得。Options validation and the ConfigureOptions overloads that accept service dependencies are only available via OptionsBuilder.

OptionsBuilder选项验证部分中使用。OptionsBuilder is used in the Options validation section.

使用 DI 服务配置选项Use DI services to configure options

在配置选项时,可以通过以下两种方式通过依赖关系注入访问服务:Services can be accessed from dependency injection while configuring options in two ways:

建议将配置委托传递给 Configure,因为创建服务较复杂。We recommend passing a configuration delegate to Configure, since creating a service is more complex. 在调用 Configure 时,创建类型等效于框架执行的操作。Creating a type is equivalent to what the framework does when calling Configure. 调用 Configure 会注册临时泛型 IConfigureNamedOptions<TOptions>,它具有接受指定的泛型服务类型的构造函数。Calling Configure registers a transient generic IConfigureNamedOptions<TOptions>, which has a constructor that accepts the generic service types specified.

选项验证Options validation

通过选项验证,可以验证选项值。Options validation enables option values to be validated.

请考虑使用以下 appsettings.json 文件:Consider the following appsettings.json file:

{
  "MyConfig": {
    "Key1": "My Key One",
    "Key2": 10,
    "Key3": 32
  }
}

下面的类绑定到 "MyConfig" 配置节,并应用若干 DataAnnotations 规则:The following class binds to the "MyConfig" configuration section and applies a couple of DataAnnotations rules:

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

下面的代码:The following code:

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 包中定义。The ValidateDataAnnotations extension method is defined in the Microsoft.Extensions.Options.DataAnnotations NuGet package. 对于使用 Microsoft.NET.Sdk.Web SDK 的 Web 应用,通过共享框架隐式引用此包。For web apps that use the Microsoft.NET.Sdk.Web SDK, this package is referenced implicitly from the shared framework.

下面的代码显示配置值或验证错误:The following code displays the configuration values or the validation errors:

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;

        try
        {
            var configValue = _config.Value;
           
        }
        catch (OptionsValidationException ex)
        {
            foreach (var failure in ex.Failures)
            {
                _logger.LogError(failure);
            }
        }
    }

    public ContentResult Index()
    {
        string msg;
        try
        {
             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);
    }

下面的代码使用委托应用更复杂的验证规则:The following code applies a more complex validation rule using a delegate:

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

用于复杂验证的 IValidateOptionsIValidateOptions for complex validation

下面的类实现了 IValidateOptions<TOptions>The following class implements 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 并将其移入类中。IValidateOptions enables moving the validation code out of StartUp and into a class.

使用前面的代码,使用以下代码在 Startup.ConfigureServices 中启用验证:Using the preceding code, validation is enabled in Startup.ConfigureServices with the following code:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<MyConfigOptions>(Configuration.GetSection(
                                        MyConfigOptions.MyConfig));
    services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
                              <MyConfigOptions>, MyConfigValidation>());
    services.AddControllersWithViews();
}

选项后期配置Options post-configuration

使用 IPostConfigureOptions<TOptions> 设置后期配置。Set post-configuration with IPostConfigureOptions<TOptions>. 进行所有 IConfigureOptions<TOptions> 配置后运行后期配置:Post-configuration runs after all IConfigureOptions<TOptions> configuration occurs:

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

PostConfigure 可用于对命名选项进行后期配置:PostConfigure is available to post-configure named options:

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

使用 PostConfigureAll 对所有配置实例进行后期配置:Use PostConfigureAll to post-configure all configuration instances:

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

在启动期间访问选项Accessing options during startup

IOptions<TOptions>IOptionsMonitor<TOptions> 可用于 Startup.Configure 中,因为在 Configure 方法执行之前已生成服务。IOptions<TOptions> and IOptionsMonitor<TOptions> can be used in Startup.Configure, since services are built before the Configure method executes.

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

不使用 Startup.ConfigureServices 中的 IOptions<TOptions>IOptionsMonitor<TOptions>Don't use IOptions<TOptions> or IOptionsMonitor<TOptions> in Startup.ConfigureServices. 由于服务注册的顺序,可能存在不一致的选项状态。An inconsistent options state may exist due to the ordering of service registrations.

ConfigurationExtensions NuGet 包Options.ConfigurationExtensions NuGet package

Microsoft.Extensions.Options.ConfigurationExtensions 包已在 ASP.NET Core 应用中隐式引用。The Microsoft.Extensions.Options.ConfigurationExtensions package is implicitly referenced in ASP.NET Core apps.

选项模式使用类来表示相关设置的组。The options pattern uses classes to represent groups of related settings. 配置设置由方案隔离到单独的类时,应用遵循两个重要软件工程原则:When configuration settings are isolated by scenario into separate classes, the app adheres to two important software engineering principles:

选项还提供验证配置数据的机制。Options also provide a mechanism to validate configuration data. 有关详细信息,请参阅选项验证部分。For more information, see the Options validation section.

查看或下载示例代码如何下载View or download sample code (how to download)

先决条件Prerequisites

引用 Microsoft.AspNetCore.App 元包或将包引用添加到 Microsoft.Extensions.Options.ConfigurationExtensions 包。Reference the Microsoft.AspNetCore.App metapackage or add a package reference to the Microsoft.Extensions.Options.ConfigurationExtensions package.

选项接口Options interfaces

IOptionsMonitor<TOptions> 用于检索选项并管理 TOptions 实例的选项通知。IOptionsMonitor<TOptions> is used to retrieve options and manage options notifications for TOptions instances. IOptionsMonitor<TOptions> 支持以下方案:IOptionsMonitor<TOptions> supports the following scenarios:

后期配置方案允许你在进行所有 IConfigureOptions<TOptions> 配置后设置或更改选项。Post-configuration scenarios allow you to set or change options after all IConfigureOptions<TOptions> configuration occurs.

IOptionsFactory<TOptions> 负责新建选项实例。IOptionsFactory<TOptions> is responsible for creating new options instances. 它具有单个 Create 方法。It has a single Create method. 默认实现采用所有已注册 IConfigureOptions<TOptions>IPostConfigureOptions<TOptions> 并首先运行所有配置,然后才进行后期配置。The default implementation takes all registered IConfigureOptions<TOptions> and IPostConfigureOptions<TOptions> and runs all the configurations first, followed by the post-configuration. 它区分 IConfigureNamedOptions<TOptions>IConfigureOptions<TOptions> 且仅调用适当的接口。It distinguishes between IConfigureNamedOptions<TOptions> and IConfigureOptions<TOptions> and only calls the appropriate interface.

IOptionsMonitorCache<TOptions>IOptionsMonitor<TOptions> 用于缓存 TOptions 实例。IOptionsMonitorCache<TOptions> is used by IOptionsMonitor<TOptions> to cache TOptions instances. IOptionsMonitorCache<TOptions> 可使监视器中的选项实例无效,以便重新计算值 (TryRemove)。The IOptionsMonitorCache<TOptions> invalidates options instances in the monitor so that the value is recomputed (TryRemove). 可以通过 TryAdd 手动引入值。Values can be manually introduced with TryAdd. 在应按需重新创建所有命名实例时使用 Clear 方法。The Clear method is used when all named instances should be recreated on demand.

IOptionsSnapshot<TOptions> 在每次请求时应重新计算选项的方案中有用。IOptionsSnapshot<TOptions> is useful in scenarios where options should be recomputed on every request. 有关详细信息,请参阅通过 IOptionsSnapshot 重新加载配置数据部分。For more information, see the Reload configuration data with IOptionsSnapshot section.

IOptions<TOptions> 可用于支持选项。IOptions<TOptions> can be used to support options. 但是,IOptions<TOptions> 不支持前面的 IOptionsMonitor<TOptions> 方案。However, IOptions<TOptions> doesn't support the preceding scenarios of IOptionsMonitor<TOptions>. 你可以在已使用 IOptions<TOptions> 接口的现有框架和库中继续使用 IOptions<TOptions>,并且不需要 IOptionsMonitor<TOptions> 提供的方案。You may continue to use IOptions<TOptions> in existing frameworks and libraries that already use the IOptions<TOptions> interface and don't require the scenarios provided by IOptionsMonitor<TOptions>.

常规选项配置General options configuration

常规选项配置已作为示例 1 在示例应用中进行了演示。General options configuration is demonstrated as Example 1 in the sample app.

选项类必须为包含公共无参数构造函数的非抽象类。An options class must be non-abstract with a public parameterless constructor. 以下类 MyOptions 具有两种属性:Option1Option2The following class, MyOptions, has two properties, Option1 and Option2. 设置默认值为可选,但以下示例中的类构造函数设置了 Option1 的默认值。Setting default values is optional, but the class constructor in the following example sets the default value of Option1. Option2 具有通过直接初始化属性设置的默认值 (Models/MyOptions.cs):Option2 has a default value set by initializing the property directly (Models/MyOptions.cs):

public class MyOptions
{
    public MyOptions()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

MyOptions 类已通过 Configure 添加到服务容器并绑定到配置:The MyOptions class is added to the service container with Configure and bound to configuration:

// Example #1: General configuration
// Register the Configuration instance which MyOptions binds against.
services.Configure<MyOptions>(Configuration);

以下页面模型通过 IOptionsMonitor<TOptions> 使用构造函数依赖关系注入来访问设置 (Pages/Index.cshtml.cs):The following page model uses constructor dependency injection with IOptionsMonitor<TOptions> to access the settings (Pages/Index.cshtml.cs):

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #1: Simple options
var option1 = _options.Option1;
var option2 = _options.Option2;
SimpleOptions = $"option1 = {option1}, option2 = {option2}";

示例的 appsettings.json 文件指定 option1option2 的值:The sample's appsettings.json file specifies values for option1 and option2:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

运行应用时,页面模型的 OnGet 方法返回显示选项类值的字符串:When the app is run, the page model's OnGet method returns a string showing the option class values:

option1 = value1_from_json, option2 = -1

备注

使用自定义 ConfigurationBuilder 从设置文件加载选项配置时,请确认基路径设置正确:When using a custom ConfigurationBuilder to load options configuration from a settings file, confirm that the base path is set correctly:

var configBuilder = new ConfigurationBuilder()
   .SetBasePath(Directory.GetCurrentDirectory())
   .AddJsonFile("appsettings.json", optional: true);
var config = configBuilder.Build();

services.Configure<MyOptions>(config);

通过 CreateDefaultBuilder 从设置文件加载选项配置时,不需要显式设置基路径。Explicitly setting the base path isn't required when loading options configuration from the settings file via CreateDefaultBuilder.

通过委托配置简单选项Configure simple options with a delegate

通过委托配置简单选项已作为示例 2 在示例应用中进行了演示。Configuring simple options with a delegate is demonstrated as Example 2 in the sample app.

使用委托设置选项值。Use a delegate to set options values. 此示例应用使用 MyOptionsWithDelegateConfig 类 (Models/MyOptionsWithDelegateConfig.cs):The sample app uses the MyOptionsWithDelegateConfig class (Models/MyOptionsWithDelegateConfig.cs):

public class MyOptionsWithDelegateConfig
{
    public MyOptionsWithDelegateConfig()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

在以下代码中,已向服务容器添加第二个 IConfigureOptions<TOptions> 服务。In the following code, a second IConfigureOptions<TOptions> service is added to the service container. 它通过 MyOptionsWithDelegateConfig 使用委托来配置绑定:It uses a delegate to configure the binding with MyOptionsWithDelegateConfig:

// Example #2: Options bound and configured by a delegate
services.Configure<MyOptionsWithDelegateConfig>(myOptions =>
{
    myOptions.Option1 = "value1_configured_by_delegate";
    myOptions.Option2 = 500;
});

Index.cshtml.cs:Index.cshtml.cs:

private readonly MyOptionsWithDelegateConfig _optionsWithDelegateConfig;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #2: Options configured by delegate
var delegate_config_option1 = _optionsWithDelegateConfig.Option1;
var delegate_config_option2 = _optionsWithDelegateConfig.Option2;
SimpleOptionsWithDelegateConfig = 
    $"delegate_option1 = {delegate_config_option1}, " +
    $"delegate_option2 = {delegate_config_option2}";

可添加多个配置提供程序。You can add multiple configuration providers. 配置提供程序可从 NuGet 包中获取,并按照注册的顺序应用。Configuration providers are available from NuGet packages and are applied in the order that they're registered. 有关详细信息,请参阅 ASP.NET Core 中的配置For more information, see ASP.NET Core 中的配置.

每次调用 Configure 都会将 IConfigureOptions<TOptions> 服务添加到服务容器。Each call to Configure adds an IConfigureOptions<TOptions> service to the service container. 在前面的示例中,Option1Option2 的值同时在 appsettings.json 中指定,但 Option1Option2 的值被配置的委托替代。In the preceding example, the values of Option1 and Option2 are both specified in appsettings.json, but the values of Option1 and Option2 are overridden by the configured delegate.

当启用多个配置服务时,指定的最后一个配置源优于其他源,由其设置配置值。When more than one configuration service is enabled, the last configuration source specified wins and sets the configuration value. 运行应用时,页面模型的 OnGet 方法返回显示选项类值的字符串:When the app is run, the page model's OnGet method returns a string showing the option class values:

delegate_option1 = value1_configured_by_delegate, delegate_option2 = 500

子选项配置Suboptions configuration

子选项配置已作为示例 3 在示例应用中进行了演示。Suboptions configuration is demonstrated as Example 3 in the sample app.

应用应创建适用于应用中特定方案组(类)的选项类。Apps should create options classes that pertain to specific scenario groups (classes) in the app. 需要配置值的部分应用应仅有权访问其使用的配置值。Parts of the app that require configuration values should only have access to the configuration values that they use.

将选项绑定到配置时,选项类型中的每个属性都将绑定到窗体 property[:sub-property:] 的配置键。When binding options to configuration, each property in the options type is bound to a configuration key of the form property[:sub-property:]. 例如,MyOptions.Option1 属性将绑定到从 appsettings.json 中的 option1 属性读取的键 Option1For example, the MyOptions.Option1 property is bound to the key Option1, which is read from the option1 property in appsettings.json.

在以下代码中,已向服务容器添加第三个 IConfigureOptions<TOptions> 服务。In the following code, a third IConfigureOptions<TOptions> service is added to the service container. 它将 MySubOptions 绑定到 appsettings.json 文件的 subsection 部分:It binds MySubOptions to the section subsection of the appsettings.json file:

// Example #3: Suboptions
// Bind options using a sub-section of the appsettings.json file.
services.Configure<MySubOptions>(Configuration.GetSection("subsection"));

GetSection 方法需要 Microsoft.Extensions.Configuration 命名空间。The GetSection method requires the Microsoft.Extensions.Configuration namespace.

示例的 appsettings.json 文件定义具有 suboption1suboption2 的键的 subsection 成员:The sample's appsettings.json file defines a subsection member with keys for suboption1 and suboption2:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

MySubOptions 类将属性 SubOption1SubOption2 定义为保留选项值 (Models/MySubOptions.cs):The MySubOptions class defines properties, SubOption1 and SubOption2, to hold the options values (Models/MySubOptions.cs):

public class MySubOptions
{
    public MySubOptions()
    {
        // Set default values.
        SubOption1 = "value1_from_ctor";
        SubOption2 = 5;
    }
    
    public string SubOption1 { get; set; }
    public int SubOption2 { get; set; }
}

页面模型的 OnGet 方法返回包含选项值的字符串 (Pages/Index.cshtml.cs):The page model's OnGet method returns a string with the options values (Pages/Index.cshtml.cs):

private readonly MySubOptions _subOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #3: Suboptions
var subOption1 = _subOptions.SubOption1;
var subOption2 = _subOptions.SubOption2;
SubOptions = $"subOption1 = {subOption1}, subOption2 = {subOption2}";

运行应用时,OnGet 方法返回显示子选项类值的字符串:When the app is run, the OnGet method returns a string showing the suboption class values:

subOption1 = subvalue1_from_json, subOption2 = 200

选项注入Options injection

选项注入已作为示例 4 在示例应用中进行了演示。Options injection is demonstrated as Example 4 in the sample app.

IOptionsMonitor<TOptions> 注入:Inject IOptionsMonitor<TOptions> into:

  • 使用 @inject Razor 指令的 Razor 页面或 MVC 视图。A Razor page or MVC view with the @inject Razor directive.
  • 页面或视图模型。A page or view model.

示例应用中的以下示例将 IOptionsMonitor<TOptions> 注入页面模型 (Pages/Index.cshtml.cs):The following example from the sample app injects IOptionsMonitor<TOptions> into a page model (Pages/Index.cshtml.cs):

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #4: Bind options directly to the page
MyOptions = _options;

示例应用演示如何使用 @inject 指令注入 IOptionsMonitor<MyOptions>The sample app shows how to inject IOptionsMonitor<MyOptions> with an @inject directive:

@page
@model IndexModel
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<MyOptions> OptionsAccessor
@{
    ViewData["Title"] = "Options Sample";
}

<h1>@ViewData["Title"]</h1>

当应用运行时,选项值显示在呈现的页面中:When the app is run, the options values are shown in the rendered page:

选项值 Option1:value1_from_json 和 Option2:-1 从模型中加载并注入视图。

通过 IOptionsSnapshot 重新加载配置数据Reload configuration data with IOptionsSnapshot

通过 IOptionsSnapshot<TOptions> 重新加载配置数据已作为示例 5 在示例应用中进行了演示。Reloading configuration data with IOptionsSnapshot<TOptions> is demonstrated in Example 5 in the sample app.

通过使用 IOptionsSnapshot<TOptions>,针对请求生存期访问和缓存选项时,每个请求都会计算一次选项。Using IOptionsSnapshot<TOptions>, options are computed once per request when accessed and cached for the lifetime of the request.

IOptionsMonitorIOptionsSnapshot 之间的区别在于:The difference between IOptionsMonitor and IOptionsSnapshot is that:

  • IOptionsMonitor 是一种单一示例服务,可随时检索当前选项值,这在单一实例依赖项中尤其有用。IOptionsMonitor is a singleton service that retrieves current option values at any time, which is especially useful in singleton dependencies.
  • IOptionsSnapshot 是一种作用域服务,并在构造 IOptionsSnapshot<T> 对象时提供选项的快照。IOptionsSnapshot is a scoped service and provides a snapshot of the options at the time the IOptionsSnapshot<T> object is constructed. 选项快照旨在用于暂时性和有作用域的依赖项。Options snapshots are designed for use with transient and scoped dependencies.

以下示例演示如何在更改 appsettings.json (Pages/Index.cshtml.cs) 后创建新的 IOptionsSnapshot<TOptions>The following example demonstrates how a new IOptionsSnapshot<TOptions> is created after appsettings.json changes (Pages/Index.cshtml.cs). 在更改文件和重新加载配置之前,针对服务器的多个请求返回 appsettings.json 文件提供的常数值。Multiple requests to the server return constant values provided by the appsettings.json file until the file is changed and configuration reloads.

private readonly MyOptions _snapshotOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #5: Snapshot options
var snapshotOption1 = _snapshotOptions.Option1;
var snapshotOption2 = _snapshotOptions.Option2;
SnapshotOptions = 
    $"snapshot option1 = {snapshotOption1}, " +
    $"snapshot option2 = {snapshotOption2}";

下图显示从 appsettings.json 文件加载的初始 option1option2 值:The following image shows the initial option1 and option2 values loaded from the appsettings.json file:

snapshot option1 = value1_from_json, snapshot option2 = -1

将 appsettings.json 文件中的值更改为 value1_from_json UPDATED200Change the values in the appsettings.json file to value1_from_json UPDATED and 200. 保存 appsettings.json 文件。Save the appsettings.json file. 刷新浏览器,查看更新的选项值:Refresh the browser to see that the options values are updated:

snapshot option1 = value1_from_json UPDATED, snapshot option2 = 200

包含 IConfigureNamedOptions 的命名选项支持Named options support with IConfigureNamedOptions

包含 IConfigureNamedOptions<TOptions> 的命名选项支持已作为示例 6 在示例应用中进行了演示。Named options support with IConfigureNamedOptions<TOptions> is demonstrated as Example 6 in the sample app.

命名选项支持允许应用在命名选项配置之间进行区分。Named options support allows the app to distinguish between named options configurations. 在示例应用中,命名选项通过 OptionsServiceCollectionExtensions.Configure 进行声明,其调用 ConfigureNamedOptions<TOptions>.Configure 扩展方法。In the sample app, named options are declared with OptionsServiceCollectionExtensions.Configure, which calls the ConfigureNamedOptions<TOptions>.Configure extension method. 命名选项区分大小写。Named options are case sensitive.

// Example #6: Named options (named_options_1)
// Register the ConfigurationBuilder instance which MyOptions binds against.
// Specify that the options loaded from configuration are named
// "named_options_1".
services.Configure<MyOptions>("named_options_1", Configuration);

// Example #6: Named options (named_options_2)
// Specify that the options loaded from the MyOptions class are named
// "named_options_2".
// Use a delegate to configure option values.
services.Configure<MyOptions>("named_options_2", myOptions =>
{
    myOptions.Option1 = "named_options_2_value1_from_action";
});

示例应用通过 Get (Pages/Index.cshtml.cs) 访问命名选项:The sample app accesses the named options with Get (Pages/Index.cshtml.cs):

private readonly MyOptions _named_options_1;
private readonly MyOptions _named_options_2;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #6: Named options
var named_options_1 = 
    $"named_options_1: option1 = {_named_options_1.Option1}, " +
    $"option2 = {_named_options_1.Option2}";
var named_options_2 = 
    $"named_options_2: option1 = {_named_options_2.Option1}, " +
    $"option2 = {_named_options_2.Option2}";
NamedOptions = $"{named_options_1} {named_options_2}";

运行示例应用,将返回命名选项:Running the sample app, the named options are returned:

named_options_1: option1 = value1_from_json, option2 = -1
named_options_2: option1 = named_options_2_value1_from_action, option2 = 5

从配置中提供从 appsettings.json 文件中加载的 named_options_1 值。named_options_1 values are provided from configuration, which are loaded from the appsettings.json file. 通过以下内容提供 named_options_2 值:named_options_2 values are provided by:

  • 针对 Option1ConfigureServices 中的 named_options_2 委托。The named_options_2 delegate in ConfigureServices for Option1.
  • MyOptions 类提供的 Option2 的默认值。The default value for Option2 provided by the MyOptions class.

使用 ConfigureAll 方法配置所有选项Configure all options with the ConfigureAll method

使用 ConfigureAll 方法配置所有选项实例。Configure all options instances with the ConfigureAll method. 以下代码将针对包含公共值的所有配置实例配置 Option1The following code configures Option1 for all configuration instances with a common value. 将以下代码手动添加到 Startup.ConfigureServices 方法:Add the following code manually to the Startup.ConfigureServices method:

services.ConfigureAll<MyOptions>(myOptions => 
{
    myOptions.Option1 = "ConfigureAll replacement value";
});

添加代码后运行示例应用将产生以下结果:Running the sample app after adding the code produces the following result:

named_options_1: option1 = ConfigureAll replacement value, option2 = -1
named_options_2: option1 = ConfigureAll replacement value, option2 = 5

备注

所有选项都是命名实例。All options are named instances. 现有 IConfigureOptions<TOptions> 实例将被视为面向 Options.DefaultName 实例,即 string.EmptyExisting IConfigureOptions<TOptions> instances are treated as targeting the Options.DefaultName instance, which is string.Empty. IConfigureNamedOptions<TOptions> 还可实现 IConfigureOptions<TOptions>IConfigureNamedOptions<TOptions> also implements IConfigureOptions<TOptions>. IOptionsFactory<TOptions> 的默认实现具有适当地使用每个实例的逻辑。The default implementation of the IOptionsFactory<TOptions> has logic to use each appropriately. null 命名选项用于面向所有命名实例而不是某一特定命名实例(ConfigureAllPostConfigureAll 使用此约定)。The null named option is used to target all of the named instances instead of a specific named instance (ConfigureAll and PostConfigureAll use this convention).

OptionsBuilder APIOptionsBuilder API

OptionsBuilder<TOptions> 用于配置 TOptions 实例。OptionsBuilder<TOptions> is used to configure TOptions instances. OptionsBuilder 简化了创建命名选项的过程,因为它只是初始 AddOptions<TOptions>(string optionsName) 调用的单个参数,而不会出现在所有后续调用中。OptionsBuilder streamlines creating named options as it's only a single parameter to the initial AddOptions<TOptions>(string optionsName) call instead of appearing in all of the subsequent calls. 选项验证和接受服务依赖关系的 ConfigureOptions 重载仅可通过 OptionsBuilder 获得。Options validation and the ConfigureOptions overloads that accept service dependencies are only available via OptionsBuilder.

// Options.DefaultName = "" is used.
services.AddOptions<MyOptions>().Configure(o => o.Property = "default");

services.AddOptions<MyOptions>("optionalName")
    .Configure(o => o.Property = "named");

使用 DI 服务配置选项Use DI services to configure options

在配置选项时,可以通过以下两种方式通过依赖关系注入访问其他服务:You can access other services from dependency injection while configuring options in two ways:

建议将配置委托传递给 Configure,因为创建服务较复杂。We recommend passing a configuration delegate to Configure, since creating a service is more complex. 在使用 Configure时,创建你自己的类型等效于框架为你执行的操作。Creating your own type is equivalent to what the framework does for you when you use Configure. 调用 Configure 会注册临时泛型 IConfigureNamedOptions<TOptions>,它具有接受指定的泛型服务类型的构造函数。Calling Configure registers a transient generic IConfigureNamedOptions<TOptions>, which has a constructor that accepts the generic service types specified.

选项验证Options validation

借助选项验证,可以验证已配置的选项。Options validation allows you to validate options when options are configured. 使用验证方法调用 Validate。如果选项有效,方法返回 true;如果无效,方法返回 falseCall Validate with a validation method that returns true if options are valid and false if they aren't valid:

// Registration
services.AddOptions<MyOptions>("optionalOptionsName")
    .Configure(o => { }) // Configure the options
    .Validate(o => YourValidationShouldReturnTrueIfValid(o), 
        "custom error");

// Consumption
var monitor = services.BuildServiceProvider()
    .GetService<IOptionsMonitor<MyOptions>>();

try
{
    var options = monitor.Get("optionalOptionsName");
}
catch (OptionsValidationException e) 
{
   // e.OptionsName returns "optionalOptionsName"
   // e.OptionsType returns typeof(MyOptions)
   // e.Failures returns a list of errors, which would contain 
   //     "custom error"
}

上面的示例将命名选项实例设置为 optionalOptionsNameThe preceding example sets the named options instance to optionalOptionsName. 默认选项实例为 Options.DefaultNameThe default options instance is Options.DefaultName.

选项验证在选项实例创建后运行。Validation runs when the options instance is created. 系统保证在选项实例首次获得访问时通过验证。An options instance is guaranteed to pass validation the first time it's accessed.

重要

选项验证无法防止在创建选项实例后发生选项修改。Options validation doesn't guard against options modifications after the options instance is created. 例如,在首次访问选项时按请求创建并验证 IOptionsSnapshot 选项。For example, IOptionsSnapshot options are created and validated once per request when the options are first accessed. 对于同一个请求,在后续访问尝试时不会再次验证 IOptionsSnapshot 选项。The IOptionsSnapshot options aren't validated again on subsequent access attempts for the same request.

Validate 方法接受 Func<TOptions, bool>The Validate method accepts a Func<TOptions, bool>. 若要完全自定义验证,请实现 IValidateOptions<TOptions>,它支持:To fully customize validation, implement IValidateOptions<TOptions>, which allows:

  • 验证多种类型的选项:class ValidateTwo : IValidateOptions<Option1>, IValidationOptions<Option2>Validation of multiple options types: class ValidateTwo : IValidateOptions<Option1>, IValidationOptions<Option2>
  • 验证取决于其他选项类型:public DependsOnAnotherOptionValidator(IOptionsMonitor<AnotherOption> options)Validation that depends on another option type: public DependsOnAnotherOptionValidator(IOptionsMonitor<AnotherOption> options)

IValidateOptions 验证:IValidateOptions validates:

  • 特定的命名选项实例。A specific named options instance.
  • 所有选项(如果 namenull 的话)。All options when name is null.

通过接口实现返回 ValidateOptionsResultReturn a ValidateOptionsResult from your implementation of the interface:

public interface IValidateOptions<TOptions> where TOptions : class
{
    ValidateOptionsResult Validate(string name, TOptions options);
}

通过调用 OptionsBuilder<TOptions> 上的 ValidateDataAnnotations 方法,可以从 Microsoft.Extensions.Options.DataAnnotations 包中获得基于数据注释的验证。Data Annotation-based validation is available from the Microsoft.Extensions.Options.DataAnnotations package by calling the ValidateDataAnnotations method on OptionsBuilder<TOptions>. Microsoft.Extensions.Options.DataAnnotations 包含在 Microsoft.AspNetCore.App 元包中。Microsoft.Extensions.Options.DataAnnotations is included in the Microsoft.AspNetCore.App metapackage.

using Microsoft.Extensions.DependencyInjection;

private class AnnotatedOptions
{
    [Required]
    public string Required { get; set; }

    [StringLength(5, ErrorMessage = "Too long.")]
    public string StringLength { get; set; }

    [Range(-5, 5, ErrorMessage = "Out of range.")]
    public int IntRange { get; set; }
}

[Fact]
public void CanValidateDataAnnotations()
{
    var services = new ServiceCollection();
    services.AddOptions<AnnotatedOptions>()
        .Configure(o =>
        {
            o.StringLength = "111111";
            o.IntRange = 10;
            o.Custom = "nowhere";
        })
        .ValidateDataAnnotations();

    var sp = services.BuildServiceProvider();

    var error = Assert.Throws<OptionsValidationException>(() => 
        sp.GetRequiredService<IOptionsMonitor<AnnotatedOptions>>().CurrentValue);
    ValidateFailure<AnnotatedOptions>(error, Options.DefaultName, 1,
        "DataAnnotation validation failed for members Required " +
            "with the error 'The Required field is required.'.",
        "DataAnnotation validation failed for members StringLength " +
            "with the error 'Too long.'.",
        "DataAnnotation validation failed for members IntRange " +
            "with the error 'Out of range.'.");
}

设计团队正在考虑为未来版本提供预先验证(在启动时快速失败)。Eager validation (fail fast at startup) is under consideration for a future release.

选项后期配置Options post-configuration

使用 IPostConfigureOptions<TOptions> 设置后期配置。Set post-configuration with IPostConfigureOptions<TOptions>. 进行所有 IConfigureOptions<TOptions> 配置后运行后期配置:Post-configuration runs after all IConfigureOptions<TOptions> configuration occurs:

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

PostConfigure 可用于对命名选项进行后期配置:PostConfigure is available to post-configure named options:

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

使用 PostConfigureAll 对所有配置实例进行后期配置:Use PostConfigureAll to post-configure all configuration instances:

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

在启动期间访问选项Accessing options during startup

IOptions<TOptions>IOptionsMonitor<TOptions> 可用于 Startup.Configure 中,因为在 Configure 方法执行之前已生成服务。IOptions<TOptions> and IOptionsMonitor<TOptions> can be used in Startup.Configure, since services are built before the Configure method executes.

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

不使用 Startup.ConfigureServices 中的 IOptions<TOptions>IOptionsMonitor<TOptions>Don't use IOptions<TOptions> or IOptionsMonitor<TOptions> in Startup.ConfigureServices. 由于服务注册的顺序,可能存在不一致的选项状态。An inconsistent options state may exist due to the ordering of service registrations.

选项模式使用类来表示相关设置的组。The options pattern uses classes to represent groups of related settings. 配置设置由方案隔离到单独的类时,应用遵循两个重要软件工程原则:When configuration settings are isolated by scenario into separate classes, the app adheres to two important software engineering principles:

选项还提供验证配置数据的机制。Options also provide a mechanism to validate configuration data. 有关详细信息,请参阅选项验证部分。For more information, see the Options validation section.

查看或下载示例代码如何下载View or download sample code (how to download)

先决条件Prerequisites

引用 Microsoft.AspNetCore.App 元包或将包引用添加到 Microsoft.Extensions.Options.ConfigurationExtensions 包。Reference the Microsoft.AspNetCore.App metapackage or add a package reference to the Microsoft.Extensions.Options.ConfigurationExtensions package.

选项接口Options interfaces

IOptionsMonitor<TOptions> 用于检索选项并管理 TOptions 实例的选项通知。IOptionsMonitor<TOptions> is used to retrieve options and manage options notifications for TOptions instances. IOptionsMonitor<TOptions> 支持以下方案:IOptionsMonitor<TOptions> supports the following scenarios:

后期配置方案允许你在进行所有 IConfigureOptions<TOptions> 配置后设置或更改选项。Post-configuration scenarios allow you to set or change options after all IConfigureOptions<TOptions> configuration occurs.

IOptionsFactory<TOptions> 负责新建选项实例。IOptionsFactory<TOptions> is responsible for creating new options instances. 它具有单个 Create 方法。It has a single Create method. 默认实现采用所有已注册 IConfigureOptions<TOptions>IPostConfigureOptions<TOptions> 并首先运行所有配置,然后才进行后期配置。The default implementation takes all registered IConfigureOptions<TOptions> and IPostConfigureOptions<TOptions> and runs all the configurations first, followed by the post-configuration. 它区分 IConfigureNamedOptions<TOptions>IConfigureOptions<TOptions> 且仅调用适当的接口。It distinguishes between IConfigureNamedOptions<TOptions> and IConfigureOptions<TOptions> and only calls the appropriate interface.

IOptionsMonitorCache<TOptions>IOptionsMonitor<TOptions> 用于缓存 TOptions 实例。IOptionsMonitorCache<TOptions> is used by IOptionsMonitor<TOptions> to cache TOptions instances. IOptionsMonitorCache<TOptions> 可使监视器中的选项实例无效,以便重新计算值 (TryRemove)。The IOptionsMonitorCache<TOptions> invalidates options instances in the monitor so that the value is recomputed (TryRemove). 可以通过 TryAdd 手动引入值。Values can be manually introduced with TryAdd. 在应按需重新创建所有命名实例时使用 Clear 方法。The Clear method is used when all named instances should be recreated on demand.

IOptionsSnapshot<TOptions> 在每次请求时应重新计算选项的方案中有用。IOptionsSnapshot<TOptions> is useful in scenarios where options should be recomputed on every request. 有关详细信息,请参阅通过 IOptionsSnapshot 重新加载配置数据部分。For more information, see the Reload configuration data with IOptionsSnapshot section.

IOptions<TOptions> 可用于支持选项。IOptions<TOptions> can be used to support options. 但是,IOptions<TOptions> 不支持前面的 IOptionsMonitor<TOptions> 方案。However, IOptions<TOptions> doesn't support the preceding scenarios of IOptionsMonitor<TOptions>. 你可以在已使用 IOptions<TOptions> 接口的现有框架和库中继续使用 IOptions<TOptions>,并且不需要 IOptionsMonitor<TOptions> 提供的方案。You may continue to use IOptions<TOptions> in existing frameworks and libraries that already use the IOptions<TOptions> interface and don't require the scenarios provided by IOptionsMonitor<TOptions>.

常规选项配置General options configuration

常规选项配置已作为示例 1 在示例应用中进行了演示。General options configuration is demonstrated as Example 1 in the sample app.

选项类必须为包含公共无参数构造函数的非抽象类。An options class must be non-abstract with a public parameterless constructor. 以下类 MyOptions 具有两种属性:Option1Option2The following class, MyOptions, has two properties, Option1 and Option2. 设置默认值为可选,但以下示例中的类构造函数设置了 Option1 的默认值。Setting default values is optional, but the class constructor in the following example sets the default value of Option1. Option2 具有通过直接初始化属性设置的默认值 (Models/MyOptions.cs):Option2 has a default value set by initializing the property directly (Models/MyOptions.cs):

public class MyOptions
{
    public MyOptions()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

MyOptions 类已通过 Configure 添加到服务容器并绑定到配置:The MyOptions class is added to the service container with Configure and bound to configuration:

// Example #1: General configuration
// Register the Configuration instance which MyOptions binds against.
services.Configure<MyOptions>(Configuration);

以下页面模型通过 IOptionsMonitor<TOptions> 使用构造函数依赖关系注入来访问设置 (Pages/Index.cshtml.cs):The following page model uses constructor dependency injection with IOptionsMonitor<TOptions> to access the settings (Pages/Index.cshtml.cs):

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #1: Simple options
var option1 = _options.Option1;
var option2 = _options.Option2;
SimpleOptions = $"option1 = {option1}, option2 = {option2}";

示例的 appsettings.json 文件指定 option1option2 的值:The sample's appsettings.json file specifies values for option1 and option2:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

运行应用时,页面模型的 OnGet 方法返回显示选项类值的字符串:When the app is run, the page model's OnGet method returns a string showing the option class values:

option1 = value1_from_json, option2 = -1

备注

使用自定义 ConfigurationBuilder 从设置文件加载选项配置时,请确认基路径设置正确:When using a custom ConfigurationBuilder to load options configuration from a settings file, confirm that the base path is set correctly:

var configBuilder = new ConfigurationBuilder()
   .SetBasePath(Directory.GetCurrentDirectory())
   .AddJsonFile("appsettings.json", optional: true);
var config = configBuilder.Build();

services.Configure<MyOptions>(config);

通过 CreateDefaultBuilder 从设置文件加载选项配置时,不需要显式设置基路径。Explicitly setting the base path isn't required when loading options configuration from the settings file via CreateDefaultBuilder.

通过委托配置简单选项Configure simple options with a delegate

通过委托配置简单选项已作为示例 2 在示例应用中进行了演示。Configuring simple options with a delegate is demonstrated as Example 2 in the sample app.

使用委托设置选项值。Use a delegate to set options values. 此示例应用使用 MyOptionsWithDelegateConfig 类 (Models/MyOptionsWithDelegateConfig.cs):The sample app uses the MyOptionsWithDelegateConfig class (Models/MyOptionsWithDelegateConfig.cs):

public class MyOptionsWithDelegateConfig
{
    public MyOptionsWithDelegateConfig()
    {
        // Set default value.
        Option1 = "value1_from_ctor";
    }
    
    public string Option1 { get; set; }
    public int Option2 { get; set; } = 5;
}

在以下代码中,已向服务容器添加第二个 IConfigureOptions<TOptions> 服务。In the following code, a second IConfigureOptions<TOptions> service is added to the service container. 它通过 MyOptionsWithDelegateConfig 使用委托来配置绑定:It uses a delegate to configure the binding with MyOptionsWithDelegateConfig:

// Example #2: Options bound and configured by a delegate
services.Configure<MyOptionsWithDelegateConfig>(myOptions =>
{
    myOptions.Option1 = "value1_configured_by_delegate";
    myOptions.Option2 = 500;
});

Index.cshtml.cs:Index.cshtml.cs:

private readonly MyOptionsWithDelegateConfig _optionsWithDelegateConfig;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #2: Options configured by delegate
var delegate_config_option1 = _optionsWithDelegateConfig.Option1;
var delegate_config_option2 = _optionsWithDelegateConfig.Option2;
SimpleOptionsWithDelegateConfig = 
    $"delegate_option1 = {delegate_config_option1}, " +
    $"delegate_option2 = {delegate_config_option2}";

可添加多个配置提供程序。You can add multiple configuration providers. 配置提供程序可从 NuGet 包中获取,并按照注册的顺序应用。Configuration providers are available from NuGet packages and are applied in the order that they're registered. 有关详细信息,请参阅 ASP.NET Core 中的配置For more information, see ASP.NET Core 中的配置.

每次调用 Configure 都会将 IConfigureOptions<TOptions> 服务添加到服务容器。Each call to Configure adds an IConfigureOptions<TOptions> service to the service container. 在前面的示例中,Option1Option2 的值同时在 appsettings.json 中指定,但 Option1Option2 的值被配置的委托替代。In the preceding example, the values of Option1 and Option2 are both specified in appsettings.json, but the values of Option1 and Option2 are overridden by the configured delegate.

当启用多个配置服务时,指定的最后一个配置源优于其他源,由其设置配置值。When more than one configuration service is enabled, the last configuration source specified wins and sets the configuration value. 运行应用时,页面模型的 OnGet 方法返回显示选项类值的字符串:When the app is run, the page model's OnGet method returns a string showing the option class values:

delegate_option1 = value1_configured_by_delegate, delegate_option2 = 500

子选项配置Suboptions configuration

子选项配置已作为示例 3 在示例应用中进行了演示。Suboptions configuration is demonstrated as Example 3 in the sample app.

应用应创建适用于应用中特定方案组(类)的选项类。Apps should create options classes that pertain to specific scenario groups (classes) in the app. 需要配置值的部分应用应仅有权访问其使用的配置值。Parts of the app that require configuration values should only have access to the configuration values that they use.

将选项绑定到配置时,选项类型中的每个属性都将绑定到窗体 property[:sub-property:] 的配置键。When binding options to configuration, each property in the options type is bound to a configuration key of the form property[:sub-property:]. 例如,MyOptions.Option1 属性将绑定到从 appsettings.json 中的 option1 属性读取的键 Option1For example, the MyOptions.Option1 property is bound to the key Option1, which is read from the option1 property in appsettings.json.

在以下代码中,已向服务容器添加第三个 IConfigureOptions<TOptions> 服务。In the following code, a third IConfigureOptions<TOptions> service is added to the service container. 它将 MySubOptions 绑定到 appsettings.json 文件的 subsection 部分:It binds MySubOptions to the section subsection of the appsettings.json file:

// Example #3: Suboptions
// Bind options using a sub-section of the appsettings.json file.
services.Configure<MySubOptions>(Configuration.GetSection("subsection"));

GetSection 方法需要 Microsoft.Extensions.Configuration 命名空间。The GetSection method requires the Microsoft.Extensions.Configuration namespace.

示例的 appsettings.json 文件定义具有 suboption1suboption2 的键的 subsection 成员:The sample's appsettings.json file defines a subsection member with keys for suboption1 and suboption2:

{
  "option1": "value1_from_json",
  "option2": -1,
  "subsection": {
    "suboption1": "subvalue1_from_json",
    "suboption2": 200
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

MySubOptions 类将属性 SubOption1SubOption2 定义为保留选项值 (Models/MySubOptions.cs):The MySubOptions class defines properties, SubOption1 and SubOption2, to hold the options values (Models/MySubOptions.cs):

public class MySubOptions
{
    public MySubOptions()
    {
        // Set default values.
        SubOption1 = "value1_from_ctor";
        SubOption2 = 5;
    }
    
    public string SubOption1 { get; set; }
    public int SubOption2 { get; set; }
}

页面模型的 OnGet 方法返回包含选项值的字符串 (Pages/Index.cshtml.cs):The page model's OnGet method returns a string with the options values (Pages/Index.cshtml.cs):

private readonly MySubOptions _subOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #3: Suboptions
var subOption1 = _subOptions.SubOption1;
var subOption2 = _subOptions.SubOption2;
SubOptions = $"subOption1 = {subOption1}, subOption2 = {subOption2}";

运行应用时,OnGet 方法返回显示子选项类值的字符串:When the app is run, the OnGet method returns a string showing the suboption class values:

subOption1 = subvalue1_from_json, subOption2 = 200

视图模型或通过直接视图注入提供的选项Options provided by a view model or with direct view injection

视图模型或通过直接视图注入提供的选项已作为示例 4 在示例应用中进行了演示。Options provided by a view model or with direct view injection is demonstrated as Example 4 in the sample app.

可在视图模型中或通过将 IOptionsMonitor<TOptions> 直接注入到视图 (Pages/Index.cshtml.cs) 来提供选项:Options can be supplied in a view model or by injecting IOptionsMonitor<TOptions> directly into a view (Pages/Index.cshtml.cs):

private readonly MyOptions _options;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #4: Bind options directly to the page
MyOptions = _options;

示例应用演示如何使用 @inject 指令注入 IOptionsMonitor<MyOptions>The sample app shows how to inject IOptionsMonitor<MyOptions> with an @inject directive:

@page
@model IndexModel
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<MyOptions> OptionsAccessor
@{
    ViewData["Title"] = "Options Sample";
}

<h1>@ViewData["Title"]</h1>

当应用运行时,选项值显示在呈现的页面中:When the app is run, the options values are shown in the rendered page:

选项值 Option1:value1_from_json 和 Option2:-1 从模型中加载并注入视图。

通过 IOptionsSnapshot 重新加载配置数据Reload configuration data with IOptionsSnapshot

通过 IOptionsSnapshot<TOptions> 重新加载配置数据已作为示例 5 在示例应用中进行了演示。Reloading configuration data with IOptionsSnapshot<TOptions> is demonstrated in Example 5 in the sample app.

IOptionsSnapshot<TOptions> 支持包含最小处理开销的重新加载选项。IOptionsSnapshot<TOptions> supports reloading options with minimal processing overhead.

针对请求生存期访问和缓存选项时,每个请求只能计算一次选项。Options are computed once per request when accessed and cached for the lifetime of the request.

以下示例演示如何在更改 appsettings.json (Pages/Index.cshtml.cs) 后创建新的 IOptionsSnapshot<TOptions>The following example demonstrates how a new IOptionsSnapshot<TOptions> is created after appsettings.json changes (Pages/Index.cshtml.cs). 在更改文件和重新加载配置之前,针对服务器的多个请求返回 appsettings.json 文件提供的常数值。Multiple requests to the server return constant values provided by the appsettings.json file until the file is changed and configuration reloads.

private readonly MyOptions _snapshotOptions;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #5: Snapshot options
var snapshotOption1 = _snapshotOptions.Option1;
var snapshotOption2 = _snapshotOptions.Option2;
SnapshotOptions = 
    $"snapshot option1 = {snapshotOption1}, " +
    $"snapshot option2 = {snapshotOption2}";

下图显示从 appsettings.json 文件加载的初始 option1option2 值:The following image shows the initial option1 and option2 values loaded from the appsettings.json file:

snapshot option1 = value1_from_json, snapshot option2 = -1

将 appsettings.json 文件中的值更改为 value1_from_json UPDATED200Change the values in the appsettings.json file to value1_from_json UPDATED and 200. 保存 appsettings.json 文件。Save the appsettings.json file. 刷新浏览器,查看更新的选项值:Refresh the browser to see that the options values are updated:

snapshot option1 = value1_from_json UPDATED, snapshot option2 = 200

包含 IConfigureNamedOptions 的命名选项支持Named options support with IConfigureNamedOptions

包含 IConfigureNamedOptions<TOptions> 的命名选项支持已作为示例 6 在示例应用中进行了演示。Named options support with IConfigureNamedOptions<TOptions> is demonstrated as Example 6 in the sample app.

命名选项支持允许应用在命名选项配置之间进行区分。Named options support allows the app to distinguish between named options configurations. 在示例应用中,命名选项通过 OptionsServiceCollectionExtensions.Configure 进行声明,其调用 ConfigureNamedOptions<TOptions>.Configure 扩展方法。In the sample app, named options are declared with OptionsServiceCollectionExtensions.Configure, which calls the ConfigureNamedOptions<TOptions>.Configure extension method. 命名选项区分大小写。Named options are case sensitive.

// Example #6: Named options (named_options_1)
// Register the ConfigurationBuilder instance which MyOptions binds against.
// Specify that the options loaded from configuration are named
// "named_options_1".
services.Configure<MyOptions>("named_options_1", Configuration);

// Example #6: Named options (named_options_2)
// Specify that the options loaded from the MyOptions class are named
// "named_options_2".
// Use a delegate to configure option values.
services.Configure<MyOptions>("named_options_2", myOptions =>
{
    myOptions.Option1 = "named_options_2_value1_from_action";
});

示例应用通过 Get (Pages/Index.cshtml.cs) 访问命名选项:The sample app accesses the named options with Get (Pages/Index.cshtml.cs):

private readonly MyOptions _named_options_1;
private readonly MyOptions _named_options_2;
public IndexModel(
    IOptionsMonitor<MyOptions> optionsAccessor, 
    IOptionsMonitor<MyOptionsWithDelegateConfig> optionsAccessorWithDelegateConfig, 
    IOptionsMonitor<MySubOptions> subOptionsAccessor, 
    IOptionsSnapshot<MyOptions> snapshotOptionsAccessor, 
    IOptionsSnapshot<MyOptions> namedOptionsAccessor)
{
    _options = optionsAccessor.CurrentValue;
    _optionsWithDelegateConfig = optionsAccessorWithDelegateConfig.CurrentValue;
    _subOptions = subOptionsAccessor.CurrentValue;
    _snapshotOptions = snapshotOptionsAccessor.Value;
    _named_options_1 = namedOptionsAccessor.Get("named_options_1");
    _named_options_2 = namedOptionsAccessor.Get("named_options_2");
}
// Example #6: Named options
var named_options_1 = 
    $"named_options_1: option1 = {_named_options_1.Option1}, " +
    $"option2 = {_named_options_1.Option2}";
var named_options_2 = 
    $"named_options_2: option1 = {_named_options_2.Option1}, " +
    $"option2 = {_named_options_2.Option2}";
NamedOptions = $"{named_options_1} {named_options_2}";

运行示例应用,将返回命名选项:Running the sample app, the named options are returned:

named_options_1: option1 = value1_from_json, option2 = -1
named_options_2: option1 = named_options_2_value1_from_action, option2 = 5

从配置中提供从 appsettings.json 文件中加载的 named_options_1 值。named_options_1 values are provided from configuration, which are loaded from the appsettings.json file. 通过以下内容提供 named_options_2 值:named_options_2 values are provided by:

  • 针对 Option1ConfigureServices 中的 named_options_2 委托。The named_options_2 delegate in ConfigureServices for Option1.
  • MyOptions 类提供的 Option2 的默认值。The default value for Option2 provided by the MyOptions class.

使用 ConfigureAll 方法配置所有选项Configure all options with the ConfigureAll method

使用 ConfigureAll 方法配置所有选项实例。Configure all options instances with the ConfigureAll method. 以下代码将针对包含公共值的所有配置实例配置 Option1The following code configures Option1 for all configuration instances with a common value. 将以下代码手动添加到 Startup.ConfigureServices 方法:Add the following code manually to the Startup.ConfigureServices method:

services.ConfigureAll<MyOptions>(myOptions => 
{
    myOptions.Option1 = "ConfigureAll replacement value";
});

添加代码后运行示例应用将产生以下结果:Running the sample app after adding the code produces the following result:

named_options_1: option1 = ConfigureAll replacement value, option2 = -1
named_options_2: option1 = ConfigureAll replacement value, option2 = 5

备注

所有选项都是命名实例。All options are named instances. 现有 IConfigureOptions<TOptions> 实例将被视为面向 Options.DefaultName 实例,即 string.EmptyExisting IConfigureOptions<TOptions> instances are treated as targeting the Options.DefaultName instance, which is string.Empty. IConfigureNamedOptions<TOptions> 还可实现 IConfigureOptions<TOptions>IConfigureNamedOptions<TOptions> also implements IConfigureOptions<TOptions>. IOptionsFactory<TOptions> 的默认实现具有适当地使用每个实例的逻辑。The default implementation of the IOptionsFactory<TOptions> has logic to use each appropriately. null 命名选项用于面向所有命名实例而不是某一特定命名实例(ConfigureAllPostConfigureAll 使用此约定)。The null named option is used to target all of the named instances instead of a specific named instance (ConfigureAll and PostConfigureAll use this convention).

OptionsBuilder APIOptionsBuilder API

OptionsBuilder<TOptions> 用于配置 TOptions 实例。OptionsBuilder<TOptions> is used to configure TOptions instances. OptionsBuilder 简化了创建命名选项的过程,因为它只是初始 AddOptions<TOptions>(string optionsName) 调用的单个参数,而不会出现在所有后续调用中。OptionsBuilder streamlines creating named options as it's only a single parameter to the initial AddOptions<TOptions>(string optionsName) call instead of appearing in all of the subsequent calls. 选项验证和接受服务依赖关系的 ConfigureOptions 重载仅可通过 OptionsBuilder 获得。Options validation and the ConfigureOptions overloads that accept service dependencies are only available via OptionsBuilder.

// Options.DefaultName = "" is used.
services.AddOptions<MyOptions>().Configure(o => o.Property = "default");

services.AddOptions<MyOptions>("optionalName")
    .Configure(o => o.Property = "named");

使用 DI 服务配置选项Use DI services to configure options

在配置选项时,可以通过以下两种方式通过依赖关系注入访问其他服务:You can access other services from dependency injection while configuring options in two ways:

建议将配置委托传递给 Configure,因为创建服务较复杂。We recommend passing a configuration delegate to Configure, since creating a service is more complex. 在使用 Configure时,创建你自己的类型等效于框架为你执行的操作。Creating your own type is equivalent to what the framework does for you when you use Configure. 调用 Configure 会注册临时泛型 IConfigureNamedOptions<TOptions>,它具有接受指定的泛型服务类型的构造函数。Calling Configure registers a transient generic IConfigureNamedOptions<TOptions>, which has a constructor that accepts the generic service types specified.

选项后期配置Options post-configuration

使用 IPostConfigureOptions<TOptions> 设置后期配置。Set post-configuration with IPostConfigureOptions<TOptions>. 进行所有 IConfigureOptions<TOptions> 配置后运行后期配置:Post-configuration runs after all IConfigureOptions<TOptions> configuration occurs:

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

PostConfigure 可用于对命名选项进行后期配置:PostConfigure is available to post-configure named options:

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

使用 PostConfigureAll 对所有配置实例进行后期配置:Use PostConfigureAll to post-configure all configuration instances:

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

在启动期间访问选项Accessing options during startup

IOptions<TOptions>IOptionsMonitor<TOptions> 可用于 Startup.Configure 中,因为在 Configure 方法执行之前已生成服务。IOptions<TOptions> and IOptionsMonitor<TOptions> can be used in Startup.Configure, since services are built before the Configure method executes.

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

不使用 Startup.ConfigureServices 中的 IOptions<TOptions>IOptionsMonitor<TOptions>Don't use IOptions<TOptions> or IOptionsMonitor<TOptions> in Startup.ConfigureServices. 由于服务注册的顺序,可能存在不一致的选项状态。An inconsistent options state may exist due to the ordering of service registrations.

其他资源Additional resources