ASP.NET Core 中的選項模式Options pattern in ASP.NET Core

作者:Luke LathamBy Luke Latham

如需此主題的 1.1 版,請下載 ASP.NET Core 中的選項模式 (1.1 版,PDF)For the 1.1 version of this topic, download Options pattern in ASP.NET Core (version 1.1, PDF).

選項模式使用類別來代表一組相關的設定。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.csIndex.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 屬性繫結至索引鍵 Option1,其是從 appsettings.json 中的 option1 屬性讀取。For 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.Options.ConfigurationExtensions NuGet 套件。The GetSection extension method requires the Microsoft.Extensions.Options.ConfigurationExtensions NuGet package. 如果應用程式使用 Microsoft.AspNetCore.App 中繼套件 (ASP.NET Core 2.1 或更新版本),則會自動包含套件。If the app uses the Microsoft.AspNetCore.App metapackage (ASP.NET Core 2.1 or later), the package is automatically included.

範例的 appsettings.json 檔案會定義 subsection 成員,並具有 suboption1suboption2 的索引鍵: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 變更之後如何建立新的 IOptionsSnapshot<TOptions> (Pages/Index.cshtml.cs)。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:

// 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

named_options_1 值會從設定提供,這會從 appsettings.json 檔案載入。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:

  • ConfigureServicesOption1named_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. 您的選項執行個體保證會在第一次存取時傳遞驗證。Your options instance is guaranteed to pass validation the first time it's accessed.

重要

選項一開始設定並驗證之後,選項驗證不會防範選項的修改。Options validation doesn't guard against options modifications after the options are initially configured and validated.

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.AspNetCore.App 中繼套件 (ASP.NET Core 2.2 或更新版本) 包含 Microsoft.Extensions.Options.DataAnnotationsMicrosoft.Extensions.Options.DataAnnotations is included in the Microsoft.AspNetCore.App metapackage (ASP.NET Core 2.2 or later).

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>>().Value);
    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.

其他資源Additional resources