ASP.NET Core 中的選項模式
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
作者:Rick Anderson。
選項模式會使用類別來提供相關設定群組的強型別存取。 當組態設定依案例隔離到不同的類別時,應用程式會遵守兩個重要的軟體工程準則:
選項也提供驗證設定資料的機制。 如需詳細資訊,請參閱選項驗證一節。
本文提供 ASP.NET Core 中的選項模式相關資訊。 如需在主控台應用程式中使用選項模式的詳細資訊,請參閱 .NET 中的選項模式。
繫結階層式設定
讀取相關設定值的慣用方式是使用選項模式 (部分機器翻譯)。 例如,為讀取下列設定值:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
建立下列 PositionOptions
類別:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
選項類別:
- 必須是非抽象的。
- 具有繫結了組態中對應項目的類型的公開讀寫屬性。
- 具有其繫結至組態中相符項目的讀寫屬性。
- 沒有繫結它的欄位。 在上述程式碼中,
Position
未繫結: 會使用Position
欄位,因此當您將類別繫結到設定提供者時,不需要以硬式編碼方式將字串"Position"
放在應用程式中。
下列程式碼範例:
- 呼叫 ConfigurationBinder.Bind 以將
PositionOptions
類別繫結到Position
區段。 - 顯示
Position
設定資料。
public class Test22Model : PageModel
{
private readonly IConfiguration Configuration;
public Test22Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
var positionOptions = new PositionOptions();
Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
ConfigurationBinder.Get<T>
會繫結並傳回指定的型別。 ConfigurationBinder.Get<T>
可能比使用 ConfigurationBinder.Bind
還方便。 下列程式碼會示範如何搭配 PositionOptions
類別使用 ConfigurationBinder.Get<T>
。
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 設定檔所做的變更。
繫結也允許抽象類別的具體化。 請考慮下列使用抽象類別 SomethingWithAName
的程式碼:
namespace ConfigSample.Options;
public abstract class SomethingWithAName
{
public abstract string? Name { get; set; }
}
public class NameTitleOptions(int age) : SomethingWithAName
{
public const string NameTitle = "NameTitle";
public override string? Name { get; set; }
public string Title { get; set; } = string.Empty;
public int Age { get; set; } = age;
}
下列程式碼會顯示 NameTitleOptions
設定值:
public class Test33Model : PageModel
{
private readonly IConfiguration Configuration;
public Test33Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
var nameTitleOptions = new NameTitleOptions(22);
Configuration.GetSection(NameTitleOptions.NameTitle).Bind(nameTitleOptions);
return Content($"Title: {nameTitleOptions.Title} \n" +
$"Name: {nameTitleOptions.Name} \n" +
$"Age: {nameTitleOptions.Age}"
);
}
}
呼叫 Bind
不像呼叫 Get<>
來得嚴格:
Bind
允許抽象的具體化。Get<>
必須建立執行個體本身。
選項模式
使用選項模式的替代方式是繫結 Position
區段並將其新增至相依性插入服務容器。 在下列程式碼中,PositionOptions
會使用 Configure 新增到服務容器,並繫結到設定:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
使用上述程式碼,下列程式碼會讀取位置選項:
public class Test2Model : PageModel
{
private readonly PositionOptions _options;
public Test2Model(IOptions<PositionOptions> options)
{
_options = options.Value;
}
public ContentResult OnGet()
{
return Content($"Title: {_options.Title} \n" +
$"Name: {_options.Name}");
}
}
在上述程式碼中,不會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。 若要讀取在應用程式啟動之後所做的變更,請使用 IOptionsSnapshot。
選項介面
- 在應該於收到每個要求時重新計算選項的案例中很實用。 如需詳細資訊,請參閱使用 IOptionsSnapshot 讀取更新的資料。
- 註冊為 Scoped,因而無法插入 Singleton 服務。
- 支援具名選項
- 用來擷取選項並管理
TOptions
執行個體的選項通知。 - 註冊為 Singleton 且可插入任何服務存留期。
- 支援:
- 變更通知
- 具名選項
- 可重新載入的設定
- 選擇性選項無效判定 (IOptionsMonitorCache<TOptions>)
設定後案例可讓您在所有 IConfigureOptions<TOptions> 設定發生時設定或變更選項。
IOptionsFactory<TOptions> 負責建立新的選項執行個體。 它有單一 Create 方法。 預設實作會接受所有已註冊的 IConfigureOptions<TOptions> 與 IPostConfigureOptions<TOptions>,並先執行所有設定,接著執行設定後作業。 它會區別 IConfigureNamedOptions<TOptions> 和 IConfigureOptions<TOptions>,且只會呼叫適當的介面。
IOptionsMonitorCache<TOptions> 會由 IOptionsMonitor<TOptions> 用來快取 TOptions
執行個體。 IOptionsMonitorCache<TOptions> 會使監視器中的選項執行個體失效,以便重新計算值 (TryRemove)。 值可以使用 TryAdd 手動導入。 Clear 方法用於應該視需要重新建立所有具名執行個體時。
使用 IOptionsSnapshot 讀取更新的資料
使用 IOptionsSnapshot<TOptions>:
- 在要求的存留期內存取及快取選項時,會針對每個要求計算一次選項。
- 可能造成顯著的效能損失,因為這是有限範圍的服務,而且會依要求重新計算。 如需詳細資訊,請參閱此 GitHub 問題和改善組態繫結的效能。
- 當使用支援讀取更新設定值的設定提供者時,會在應用程式啟動後讀取設定的變更。
IOptionsMonitor
與 IOptionsSnapshot
之間的差異在於:
IOptionsMonitor
是可隨時擷取目前選項值的 Singleton 服務,這一點在 Singleton 相依性方面特別有用。IOptionsSnapshot
是有限範圍服務,會在建構IOptionsSnapshot<T>
物件時提供選項的快照集。 選項快照集的設計目的是要與暫時性和有限範圍的相依性搭配使用。
下列程式碼會使用 IOptionsSnapshot<TOptions>。
public class TestSnapModel : PageModel
{
private readonly MyOptions _snapshotOptions;
public TestSnapModel(IOptionsSnapshot<MyOptions> snapshotOptionsAccessor)
{
_snapshotOptions = snapshotOptionsAccessor.Value;
}
public ContentResult OnGet()
{
return Content($"Option1: {_snapshotOptions.Option1} \n" +
$"Option2: {_snapshotOptions.Option2}");
}
}
下列程式碼會註冊 MyOptions
繫結的設定執行個體:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
在上述程式碼中,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
IOptionsMonitor
下列程式碼會註冊 MyOptions
繫結的設定執行個體。
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
下列範例會使用 IOptionsMonitor<TOptions>:
public class TestMonitorModel : PageModel
{
private readonly IOptionsMonitor<MyOptions> _optionsDelegate;
public TestMonitorModel(IOptionsMonitor<MyOptions> optionsDelegate )
{
_optionsDelegate = optionsDelegate;
}
public ContentResult OnGet()
{
return Content($"Option1: {_optionsDelegate.CurrentValue.Option1} \n" +
$"Option2: {_optionsDelegate.CurrentValue.Option2}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
使用 IConfigureNamedOptions 的具名選項支援
具名選項:
- 當多個設定區段繫結至相同的屬性時會很有用。
- 會區分大小寫。
請考量下列 appsettings.json
檔案:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
不要建立兩個類別來繫結 TopItem:Month
和 TopItem:Year
,而是為各個區段使用以下類別:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string Name { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
}
下列程式碼會設定具名選項:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
var app = builder.Build();
下列程式碼會顯示具名選項:
public class TestNOModel : PageModel
{
private readonly TopItemSettings _monthTopItem;
private readonly TopItemSettings _yearTopItem;
public TestNOModel(IOptionsSnapshot<TopItemSettings> namedOptionsAccessor)
{
_monthTopItem = namedOptionsAccessor.Get(TopItemSettings.Month);
_yearTopItem = namedOptionsAccessor.Get(TopItemSettings.Year);
}
public ContentResult OnGet()
{
return Content($"Month:Name {_monthTopItem.Name} \n" +
$"Month:Model {_monthTopItem.Model} \n\n" +
$"Year:Name {_yearTopItem.Name} \n" +
$"Year:Model {_yearTopItem.Model} \n" );
}
}
所有選項都是具名執行個體。 IConfigureOptions<TOptions> 執行個體會視為以 Options.DefaultName
執行個體為目標,也就是 string.Empty
。 IConfigureNamedOptions<TOptions> 也會實作 IConfigureOptions<TOptions>。 IOptionsFactory<TOptions> 的預設實作有邏輯可以適當地使用每個項目。 null
具名選項用來以所有具名執行個體為目標,而不是特定的具名執行個體。 ConfigureAll 和 PostConfigureAll 都使用了這個慣例。
OptionsBuilder API
OptionsBuilder<TOptions> 會用於設定 TOptions
執行個體。 因為 OptionsBuilder
僅為初始 AddOptions<TOptions>(string optionsName)
呼叫的單一參數,而不是出現在所有後續呼叫的參數,所以其可簡化建立具名選項的程序。 選項驗證及接受服務依存性的 ConfigureOptions
多載,只可透過 OptionsBuilder
使用。
選項驗證一節所使用的是 OptionsBuilder
。
如需新增自訂存放庫 的資訊,請參閱 使用 AddOptions 來設定自訂存放庫。
使用 DI 服務來設定選項
服務可以透過相依性插入存取,並且以兩種方式設定選項:
將組態委派傳遞至 OptionsBuilder<TOptions> 上的 Configure。
OptionsBuilder<TOptions>
提供 Configure 的多載,可讓您最多使用五個服務來設定選項:builder.Services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
建立一個能實作 IConfigureOptions<TOptions> 或 IConfigureNamedOptions<TOptions> 的類型,並將該類型註冊為服務。
我們建議您將設定委派傳遞到 Configure,因為建立服務更複雜。 建立一個類型,相當於在呼叫 Configure 時架構所進行的動作。 呼叫 Configure 會註冊暫時性泛型 IConfigureNamedOptions<TOptions>,其具有會接受所指定泛型服務類型的建構函式。
選項驗證
選項驗證可驗證選項值。
請考量下列 appsettings.json
檔案:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
下列類別用於繫結至 "MyConfig"
設定區段並套用數個 DataAnnotations
規則:
public class MyConfigOptions
{
public const string MyConfig = "MyConfig";
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
public string Key1 { get; set; }
[Range(0, 1000,
ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int Key2 { get; set; }
public int Key3 { get; set; }
}
下列程式碼範例:
- 呼叫 AddOptions 以取得繫結至
MyConfigOptions
類別的 OptionsBuilder<TOptions>。 - 使用
DataAnnotations
來呼叫 ValidateDataAnnotations 以啟用驗證。
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations();
var app = builder.Build();
ValidateDataAnnotations
擴充方法定義在 Microsoft.Extensions.Options.DataAnnotations NuGet 套件中。 對於使用 Microsoft.NET.Sdk.Web
SDK 的 Web 應用程式,此套件會從共用架構隱含參考。
下列程式碼會顯示設定值或驗證錯誤:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IOptions<MyConfigOptions> _config;
public HomeController(IOptions<MyConfigOptions> config,
ILogger<HomeController> logger)
{
_config = config;
_logger = logger;
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);
}
下列程式碼會使用委派來套用更複雜的驗證規則:
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Key2 != 0)
{
return config.Key3 > config.Key2;
}
return true;
}, "Key3 must be > than Key2."); // Failure message.
var app = builder.Build();
IValidateOptions<TOptions>
和 IValidatableObject
下列類別會實作 IValidateOptions<TOptions>:
public class MyConfigValidation : IValidateOptions<MyConfigOptions>
{
public MyConfigOptions _config { get; private set; }
public MyConfigValidation(IConfiguration config)
{
_config = config.GetSection(MyConfigOptions.MyConfig)
.Get<MyConfigOptions>();
}
public ValidateOptionsResult Validate(string name, MyConfigOptions options)
{
string? vor = null;
var rx = new Regex(@"^[a-zA-Z''-'\s]{1,40}$");
var match = rx.Match(options.Key1!);
if (string.IsNullOrEmpty(match.Value))
{
vor = $"{options.Key1} doesn't match RegEx \n";
}
if ( options.Key2 < 0 || options.Key2 > 1000)
{
vor = $"{options.Key2} doesn't match Range 0 - 1000 \n";
}
if (_config.Key2 != default)
{
if(_config.Key3 <= _config.Key2)
{
vor += "Key3 must be > than Key2.";
}
}
if (vor != null)
{
return ValidateOptionsResult.Fail(vor);
}
return ValidateOptionsResult.Success;
}
}
IValidateOptions
可讓您將驗證碼移出 Program.cs
並移至一個類別。
使用上述程式碼時,會使用下列程式碼在 Program.cs
中啟用驗證:
using Microsoft.Extensions.Options;
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<MyConfigOptions>(builder.Configuration.GetSection(
MyConfigOptions.MyConfig));
builder.Services.AddSingleton<IValidateOptions
<MyConfigOptions>, MyConfigValidation>();
var app = builder.Build();
選項驗證也支援 IValidatableObject。 若要在類別本身執行類別的類別層級驗證:
- 在類別內實作
IValidatableObject
介面及其 Validate 方法。 - 在
Program.cs
中呼叫 ValidateDataAnnotations。
ValidateOnStart
選項驗證會在第一次建立 IOptions<TOptions>、IOptionsSnapshot<TOptions> 或 IOptionsMonitor<TOptions> 實作時執行。 若渴望執行選項驗證,請在應用程式啟動時,在 Program.cs
中呼叫 ValidateOnStart:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
選項設定後作業
使用 IPostConfigureOptions<TOptions> 來設定設定後作業。 設定後作業會在所有 IConfigureOptions<TOptions> 設定發生後執行:
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
builder.Services.PostConfigure<MyConfigOptions>(myOptions =>
{
myOptions.Key1 = "post_configured_key1_value";
});
PostConfigure 可用來後置設定具名選項:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
builder.Services.PostConfigure<TopItemSettings>("Month", myOptions =>
{
myOptions.Name = "post_configured_name_value";
myOptions.Model = "post_configured_model_value";
});
var app = builder.Build();
使用 PostConfigureAll 後置設定所有設定執行個體:
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
builder.Services.PostConfigureAll<MyConfigOptions>(myOptions =>
{
myOptions.Key1 = "post_configured_key1_value";
});
Program.cs
中的存取選項
若要存取 Program.cs
中的 IOptions<TOptions> 或 IOptionsMonitor<TOptions>,請在 WebApplication.Services 上呼叫 GetRequiredService:
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
其他資源
- 檢視或下載範例程式碼 \(英文\) (如何下載)
作者:Kirk Larkin 和 Rick Anderson。
選項模式會使用類別來提供相關設定群組的強型別存取。 當組態設定依案例隔離到不同的類別時,應用程式會遵守兩個重要的軟體工程準則:
選項也提供驗證設定資料的機制。 如需詳細資訊,請參閱選項驗證一節。
本文提供 ASP.NET Core 中的選項模式相關資訊。 如需在主控台應用程式中使用選項模式的詳細資訊,請參閱 .NET 中的選項模式。
繫結階層式設定
讀取相關設定值的慣用方式是使用選項模式 (部分機器翻譯)。 例如,為讀取下列設定值:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
建立下列 PositionOptions
類別:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
選項類別:
- 必須為非抽象,且具有公用的無參數建構函式。
- 型別的所有公用讀取/寫入屬性都會繫結。
- 欄位未繫結。 在上述程式碼中,
Position
未繫結: 會使用Position
欄位,因此當您將類別繫結到設定提供者時,不需要以硬式編碼方式將字串"Position"
放在應用程式中。
下列程式碼範例:
- 呼叫 ConfigurationBinder.Bind 以將
PositionOptions
類別繫結到Position
區段。 - 顯示
Position
設定資料。
public class Test22Model : PageModel
{
private readonly IConfiguration Configuration;
public Test22Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
var positionOptions = new PositionOptions();
Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
ConfigurationBinder.Get<T>
會繫結並傳回指定的型別。 ConfigurationBinder.Get<T>
可能比使用 ConfigurationBinder.Bind
還方便。 下列程式碼會示範如何搭配 PositionOptions
類別使用 ConfigurationBinder.Get<T>
。
public class Test21Model : PageModel
{
private readonly IConfiguration Configuration;
public PositionOptions? positionOptions { get; private set; }
public Test21Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
positionOptions = Configuration.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
使用選項模式的替代方式是繫結 Position
區段並將其新增至相依性插入服務容器。 在下列程式碼中,PositionOptions
會使用 Configure 新增到服務容器,並繫結到設定:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
使用上述程式碼,下列程式碼會讀取位置選項:
public class Test2Model : PageModel
{
private readonly PositionOptions _options;
public Test2Model(IOptions<PositionOptions> options)
{
_options = options.Value;
}
public ContentResult OnGet()
{
return Content($"Title: {_options.Title} \n" +
$"Name: {_options.Name}");
}
}
在上述程式碼中,不會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。 若要讀取在應用程式啟動之後所做的變更,請使用 IOptionsSnapshot。
選項介面
- 在應該於收到每個要求時重新計算選項的案例中很實用。 如需詳細資訊,請參閱使用 IOptionsSnapshot 讀取更新的資料。
- 註冊為 Scoped,因而無法插入 Singleton 服務。
- 支援具名選項
- 用來擷取選項並管理
TOptions
執行個體的選項通知。 - 註冊為 Singleton 且可插入任何服務存留期。
- 支援:
- 變更通知
- 具名選項
- 可重新載入的設定
- 選擇性選項無效判定 (IOptionsMonitorCache<TOptions>)
設定後案例可讓您在所有 IConfigureOptions<TOptions> 設定發生時設定或變更選項。
IOptionsFactory<TOptions> 負責建立新的選項執行個體。 它有單一 Create 方法。 預設實作會接受所有已註冊的 IConfigureOptions<TOptions> 與 IPostConfigureOptions<TOptions>,並先執行所有設定,接著執行設定後作業。 它會區別 IConfigureNamedOptions<TOptions> 和 IConfigureOptions<TOptions>,且只會呼叫適當的介面。
IOptionsMonitorCache<TOptions> 會由 IOptionsMonitor<TOptions> 用來快取 TOptions
執行個體。 IOptionsMonitorCache<TOptions> 會使監視器中的選項執行個體失效,以便重新計算值 (TryRemove)。 值可以使用 TryAdd 手動導入。 Clear 方法用於應該視需要重新建立所有具名執行個體時。
使用 IOptionsSnapshot 讀取更新的資料
使用 IOptionsSnapshot<TOptions>:
- 在要求的存留期內存取及快取選項時,會針對每個要求計算一次選項。
- 可能造成顯著的效能損失,因為這是有限範圍的服務,而且會依要求重新計算。 如需詳細資訊,請參閱此 GitHub 問題和改善組態繫結的效能。
- 當使用支援讀取更新設定值的設定提供者時,會在應用程式啟動後讀取設定的變更。
IOptionsMonitor
與 IOptionsSnapshot
之間的差異在於:
IOptionsMonitor
是可隨時擷取目前選項值的 Singleton 服務,這一點在 Singleton 相依性方面特別有用。IOptionsSnapshot
是有限範圍服務,會在建構IOptionsSnapshot<T>
物件時提供選項的快照集。 選項快照集的設計目的是要與暫時性和有限範圍的相依性搭配使用。
下列程式碼會使用 IOptionsSnapshot<TOptions>。
public class TestSnapModel : PageModel
{
private readonly MyOptions _snapshotOptions;
public TestSnapModel(IOptionsSnapshot<MyOptions> snapshotOptionsAccessor)
{
_snapshotOptions = snapshotOptionsAccessor.Value;
}
public ContentResult OnGet()
{
return Content($"Option1: {_snapshotOptions.Option1} \n" +
$"Option2: {_snapshotOptions.Option2}");
}
}
下列程式碼會註冊 MyOptions
繫結的設定執行個體:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
在上述程式碼中,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
IOptionsMonitor
下列程式碼會註冊 MyOptions
繫結的設定執行個體。
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
下列範例會使用 IOptionsMonitor<TOptions>:
public class TestMonitorModel : PageModel
{
private readonly IOptionsMonitor<MyOptions> _optionsDelegate;
public TestMonitorModel(IOptionsMonitor<MyOptions> optionsDelegate )
{
_optionsDelegate = optionsDelegate;
}
public ContentResult OnGet()
{
return Content($"Option1: {_optionsDelegate.CurrentValue.Option1} \n" +
$"Option2: {_optionsDelegate.CurrentValue.Option2}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
使用 IConfigureNamedOptions 的具名選項支援
具名選項:
- 當多個設定區段繫結至相同的屬性時會很有用。
- 會區分大小寫。
請考量下列 appsettings.json
檔案:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
不要建立兩個類別來繫結 TopItem:Month
和 TopItem:Year
,而是為各個區段使用以下類別:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string Name { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
}
下列程式碼會設定具名選項:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
var app = builder.Build();
下列程式碼會顯示具名選項:
public class TestNOModel : PageModel
{
private readonly TopItemSettings _monthTopItem;
private readonly TopItemSettings _yearTopItem;
public TestNOModel(IOptionsSnapshot<TopItemSettings> namedOptionsAccessor)
{
_monthTopItem = namedOptionsAccessor.Get(TopItemSettings.Month);
_yearTopItem = namedOptionsAccessor.Get(TopItemSettings.Year);
}
public ContentResult OnGet()
{
return Content($"Month:Name {_monthTopItem.Name} \n" +
$"Month:Model {_monthTopItem.Model} \n\n" +
$"Year:Name {_yearTopItem.Name} \n" +
$"Year:Model {_yearTopItem.Model} \n" );
}
}
所有選項都是具名執行個體。 IConfigureOptions<TOptions> 執行個體會視為以 Options.DefaultName
執行個體為目標,也就是 string.Empty
。 IConfigureNamedOptions<TOptions> 也會實作 IConfigureOptions<TOptions>。 IOptionsFactory<TOptions> 的預設實作有邏輯可以適當地使用每個項目。 null
具名選項用來以所有具名執行個體為目標,而不是特定的具名執行個體。 ConfigureAll 和 PostConfigureAll 都使用了這個慣例。
OptionsBuilder API
OptionsBuilder<TOptions> 會用於設定 TOptions
執行個體。 因為 OptionsBuilder
僅為初始 AddOptions<TOptions>(string optionsName)
呼叫的單一參數,而不是出現在所有後續呼叫的參數,所以其可簡化建立具名選項的程序。 選項驗證及接受服務依存性的 ConfigureOptions
多載,只可透過 OptionsBuilder
使用。
選項驗證一節所使用的是 OptionsBuilder
。
如需新增自訂存放庫 的資訊,請參閱 使用 AddOptions 來設定自訂存放庫。
使用 DI 服務來設定選項
服務可以透過相依性插入存取,並且以兩種方式設定選項:
將組態委派傳遞至 OptionsBuilder<TOptions> 上的 Configure。
OptionsBuilder<TOptions>
提供 Configure 的多載,可讓您最多使用五個服務來設定選項:builder.Services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
建立一個能實作 IConfigureOptions<TOptions> 或 IConfigureNamedOptions<TOptions> 的類型,並將該類型註冊為服務。
我們建議您將設定委派傳遞到 Configure,因為建立服務更複雜。 建立一個類型,相當於在呼叫 Configure 時架構所進行的動作。 呼叫 Configure 會註冊暫時性泛型 IConfigureNamedOptions<TOptions>,其具有會接受所指定泛型服務類型的建構函式。
選項驗證
選項驗證可驗證選項值。
請考量下列 appsettings.json
檔案:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
下列類別用於繫結至 "MyConfig"
設定區段並套用數個 DataAnnotations
規則:
public class MyConfigOptions
{
public const string MyConfig = "MyConfig";
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
public string Key1 { get; set; }
[Range(0, 1000,
ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int Key2 { get; set; }
public int Key3 { get; set; }
}
下列程式碼範例:
- 呼叫 AddOptions 以取得繫結至
MyConfigOptions
類別的 OptionsBuilder<TOptions>。 - 使用
DataAnnotations
來呼叫 ValidateDataAnnotations 以啟用驗證。
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations();
var app = builder.Build();
ValidateDataAnnotations
擴充方法定義在 Microsoft.Extensions.Options.DataAnnotations NuGet 套件中。 對於使用 Microsoft.NET.Sdk.Web
SDK 的 Web 應用程式,此套件會從共用架構隱含參考。
下列程式碼會顯示設定值或驗證錯誤:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IOptions<MyConfigOptions> _config;
public HomeController(IOptions<MyConfigOptions> config,
ILogger<HomeController> logger)
{
_config = config;
_logger = logger;
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);
}
下列程式碼會使用委派來套用更複雜的驗證規則:
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Key2 != 0)
{
return config.Key3 > config.Key2;
}
return true;
}, "Key3 must be > than Key2."); // Failure message.
var app = builder.Build();
IValidateOptions<TOptions>
和 IValidatableObject
下列類別會實作 IValidateOptions<TOptions>:
public class MyConfigValidation : IValidateOptions<MyConfigOptions>
{
public MyConfigOptions _config { get; private set; }
public MyConfigValidation(IConfiguration config)
{
_config = config.GetSection(MyConfigOptions.MyConfig)
.Get<MyConfigOptions>();
}
public ValidateOptionsResult Validate(string name, MyConfigOptions options)
{
string? vor = null;
var rx = new Regex(@"^[a-zA-Z''-'\s]{1,40}$");
var match = rx.Match(options.Key1!);
if (string.IsNullOrEmpty(match.Value))
{
vor = $"{options.Key1} doesn't match RegEx \n";
}
if ( options.Key2 < 0 || options.Key2 > 1000)
{
vor = $"{options.Key2} doesn't match Range 0 - 1000 \n";
}
if (_config.Key2 != default)
{
if(_config.Key3 <= _config.Key2)
{
vor += "Key3 must be > than Key2.";
}
}
if (vor != null)
{
return ValidateOptionsResult.Fail(vor);
}
return ValidateOptionsResult.Success;
}
}
IValidateOptions
可讓您將驗證碼移出 Program.cs
並移至一個類別。
使用上述程式碼時,會使用下列程式碼在 Program.cs
中啟用驗證:
using Microsoft.Extensions.Options;
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<MyConfigOptions>(builder.Configuration.GetSection(
MyConfigOptions.MyConfig));
builder.Services.AddSingleton<IValidateOptions
<MyConfigOptions>, MyConfigValidation>();
var app = builder.Build();
選項驗證也支援 IValidatableObject。 若要在類別本身執行類別的類別層級驗證:
- 在類別內實作
IValidatableObject
介面及其 Validate 方法。 - 在
Program.cs
中呼叫 ValidateDataAnnotations。
ValidateOnStart
選項驗證會在第一次建立 IOptions<TOptions>、IOptionsSnapshot<TOptions> 或 IOptionsMonitor<TOptions> 實作時執行。 若渴望執行選項驗證,請在應用程式啟動時,在 Program.cs
中呼叫 ValidateOnStart:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
選項設定後作業
使用 IPostConfigureOptions<TOptions> 來設定設定後作業。 設定後作業會在所有 IConfigureOptions<TOptions> 設定發生後執行:
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
builder.Services.PostConfigure<MyConfigOptions>(myOptions =>
{
myOptions.Key1 = "post_configured_key1_value";
});
PostConfigure 可用來後置設定具名選項:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
builder.Services.PostConfigure<TopItemSettings>("Month", myOptions =>
{
myOptions.Name = "post_configured_name_value";
myOptions.Model = "post_configured_model_value";
});
var app = builder.Build();
使用 PostConfigureAll 後置設定所有設定執行個體:
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig));
builder.Services.PostConfigureAll<MyConfigOptions>(myOptions =>
{
myOptions.Key1 = "post_configured_key1_value";
});
Program.cs
中的存取選項
若要存取 Program.cs
中的 IOptions<TOptions> 或 IOptionsMonitor<TOptions>,請在 WebApplication.Services 上呼叫 GetRequiredService:
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
其他資源
- 檢視或下載範例程式碼 \(英文\) (如何下載)
作者:Kirk Larkin 和 Rick Anderson。
選項模式會使用類別來提供相關設定群組的強型別存取。 當組態設定依案例隔離到不同的類別時,應用程式會遵守兩個重要的軟體工程準則:
選項也提供驗證設定資料的機制。 如需詳細資訊,請參閱選項驗證一節。
本主題提供 ASP.NET Core 中的選項模式相關資訊。 如需在主控台應用程式中使用選項模式的詳細資訊,請參閱 .NET 中的選項模式。
檢視或下載範例程式碼 \(英文\) (如何下載)
繫結階層式設定
讀取相關設定值的慣用方式是使用選項模式 (部分機器翻譯)。 例如,為讀取下列設定值:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
建立下列 PositionOptions
類別:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; }
public string Name { get; set; }
}
選項類別:
- 必須為非抽象,且具有公用的無參數建構函式。
- 型別的所有公用讀取/寫入屬性都會繫結。
- 欄位未繫結。 在上述程式碼中,
Position
未繫結: 會使用Position
屬性,因此當您將類別繫結到設定提供者時,不需要以硬式編碼方式將字串"Position"
放在應用程式中。
下列程式碼範例:
- 呼叫 ConfigurationBinder.Bind 以將
PositionOptions
類別繫結到Position
區段。 - 顯示
Position
設定資料。
public class Test22Model : PageModel
{
private readonly IConfiguration Configuration;
public Test22Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
var positionOptions = new PositionOptions();
Configuration.GetSection(PositionOptions.Position).Bind(positionOptions);
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
ConfigurationBinder.Get<T>
會繫結並傳回指定的型別。 ConfigurationBinder.Get<T>
可能比使用 ConfigurationBinder.Bind
還方便。 下列程式碼會示範如何搭配 PositionOptions
類別使用 ConfigurationBinder.Get<T>
。
public class Test21Model : PageModel
{
private readonly IConfiguration Configuration;
public PositionOptions positionOptions { get; private set; }
public Test21Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
positionOptions = Configuration.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
使用選項模式的替代方式是繫結 Position
區段並將其新增至相依性插入服務容器。 在下列程式碼中,PositionOptions
會使用 Configure 新增到服務容器,並繫結到設定:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
使用上述程式碼,下列程式碼會讀取位置選項:
public class Test2Model : PageModel
{
private readonly PositionOptions _options;
public Test2Model(IOptions<PositionOptions> options)
{
_options = options.Value;
}
public ContentResult OnGet()
{
return Content($"Title: {_options.Title} \n" +
$"Name: {_options.Name}");
}
}
在上述程式碼中,不會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。 若要讀取在應用程式啟動之後所做的變更,請使用 IOptionsSnapshot。
選項介面
- 在應該於收到每個要求時重新計算選項的案例中很實用。 如需詳細資訊,請參閱使用 IOptionsSnapshot 讀取更新的資料。
- 註冊為 Scoped 而無法插入 Singleton 服務。
- 支援具名選項
- 用來擷取選項並管理
TOptions
執行個體的選項通知。 - 註冊為 Singleton 且可插入任何服務存留期。
- 支援:
- 變更通知
- 具名選項
- 可重新載入的設定
- 選擇性選項無效判定 (IOptionsMonitorCache<TOptions>)
設定後案例可讓您在所有 IConfigureOptions<TOptions> 設定發生時設定或變更選項。
IOptionsFactory<TOptions> 負責建立新的選項執行個體。 它有單一 Create 方法。 預設實作會接受所有已註冊的 IConfigureOptions<TOptions> 與 IPostConfigureOptions<TOptions>,並先執行所有設定,接著執行設定後作業。 它會區別 IConfigureNamedOptions<TOptions> 和 IConfigureOptions<TOptions>,且只會呼叫適當的介面。
IOptionsMonitorCache<TOptions> 會由 IOptionsMonitor<TOptions> 用來快取 TOptions
執行個體。 IOptionsMonitorCache<TOptions> 會使監視器中的選項執行個體失效,以便重新計算值 (TryRemove)。 值可以使用 TryAdd 手動導入。 Clear 方法用於應該視需要重新建立所有具名執行個體時。
使用 IOptionsSnapshot 讀取更新的資料
使用 IOptionsSnapshot<TOptions>,在要求的存留期內存取及快取選項時,會針對每個要求計算一次選項。 當使用支援讀取更新設定值的設定提供者時,會在應用程式啟動後讀取設定的變更。
IOptionsMonitor
與 IOptionsSnapshot
之間的差異在於:
IOptionsMonitor
是可隨時擷取目前選項值的 Singleton 服務,這一點在 Singleton 相依性方面特別有用。IOptionsSnapshot
是有限範圍服務,會在建構IOptionsSnapshot<T>
物件時提供選項的快照集。 選項快照集的設計目的是要與暫時性和有限範圍的相依性搭配使用。
下列程式碼會使用 IOptionsSnapshot<TOptions>。
public class TestSnapModel : PageModel
{
private readonly MyOptions _snapshotOptions;
public TestSnapModel(IOptionsSnapshot<MyOptions> snapshotOptionsAccessor)
{
_snapshotOptions = snapshotOptionsAccessor.Value;
}
public ContentResult OnGet()
{
return Content($"Option1: {_snapshotOptions.Option1} \n" +
$"Option2: {_snapshotOptions.Option2}");
}
}
下列程式碼會註冊 MyOptions
繫結的設定執行個體:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
在上述程式碼中,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
IOptionsMonitor
下列程式碼會註冊 MyOptions
繫結的設定執行個體。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
下列範例會使用 IOptionsMonitor<TOptions>:
public class TestMonitorModel : PageModel
{
private readonly IOptionsMonitor<MyOptions> _optionsDelegate;
public TestMonitorModel(IOptionsMonitor<MyOptions> optionsDelegate )
{
_optionsDelegate = optionsDelegate;
}
public ContentResult OnGet()
{
return Content($"Option1: {_optionsDelegate.CurrentValue.Option1} \n" +
$"Option2: {_optionsDelegate.CurrentValue.Option2}");
}
}
在上述程式碼中,根據預設值,會讀取在應用程式啟動之後對 JSON 設定檔所做的變更。
使用 IConfigureNamedOptions 的具名選項支援
具名選項:
- 當多個設定區段繫結至相同的屬性時會很有用。
- 會區分大小寫。
請考量下列 appsettings.json
檔案:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
不要建立兩個類別來繫結 TopItem:Month
和 TopItem:Year
,而是為各個區段使用以下類別:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string Name { get; set; }
public string Model { get; set; }
}
下列程式碼會設定具名選項:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<TopItemSettings>(TopItemSettings.Month,
Configuration.GetSection("TopItem:Month"));
services.Configure<TopItemSettings>(TopItemSettings.Year,
Configuration.GetSection("TopItem:Year"));
services.AddRazorPages();
}
下列程式碼會顯示具名選項:
public class TestNOModel : PageModel
{
private readonly TopItemSettings _monthTopItem;
private readonly TopItemSettings _yearTopItem;
public TestNOModel(IOptionsSnapshot<TopItemSettings> namedOptionsAccessor)
{
_monthTopItem = namedOptionsAccessor.Get(TopItemSettings.Month);
_yearTopItem = namedOptionsAccessor.Get(TopItemSettings.Year);
}
public ContentResult OnGet()
{
return Content($"Month:Name {_monthTopItem.Name} \n" +
$"Month:Model {_monthTopItem.Model} \n\n" +
$"Year:Name {_yearTopItem.Name} \n" +
$"Year:Model {_yearTopItem.Model} \n" );
}
}
所有選項都是具名執行個體。 IConfigureOptions<TOptions> 執行個體會視為以 Options.DefaultName
執行個體為目標,也就是 string.Empty
。 IConfigureNamedOptions<TOptions> 也會實作 IConfigureOptions<TOptions>。 IOptionsFactory<TOptions> 的預設實作有邏輯可以適當地使用每個項目。 null
具名選項用來以所有具名執行個體為目標,而不是特定的具名執行個體。 ConfigureAll 和 PostConfigureAll 都使用了這個慣例。
OptionsBuilder API
OptionsBuilder<TOptions> 會用於設定 TOptions
執行個體。 因為 OptionsBuilder
僅為初始 AddOptions<TOptions>(string optionsName)
呼叫的單一參數,而不是出現在所有後續呼叫的參數,所以其可簡化建立具名選項的程序。 選項驗證及接受服務依存性的 ConfigureOptions
多載,只可透過 OptionsBuilder
使用。
選項驗證一節所使用的是 OptionsBuilder
。
如需新增自訂存放庫 的資訊,請參閱 使用 AddOptions 來設定自訂存放庫。
使用 DI 服務來設定選項
服務可以透過相依性插入存取,並且以兩種方式設定選項:
將組態委派傳遞至 OptionsBuilder<TOptions> 上的 Configure。
OptionsBuilder<TOptions>
提供 Configure 的多載,可讓您最多使用五個服務來設定選項:services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
建立一個能實作 IConfigureOptions<TOptions> 或 IConfigureNamedOptions<TOptions> 的類型,並將該類型註冊為服務。
我們建議您將設定委派傳遞到 Configure,因為建立服務更複雜。 建立一個類型,相當於在呼叫 Configure 時架構所進行的動作。 呼叫 Configure 會註冊暫時性泛型 IConfigureNamedOptions<TOptions>,其具有會接受所指定泛型服務類型的建構函式。
選項驗證
選項驗證可驗證選項值。
請考量下列 appsettings.json
檔案:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
下列類別會繫結至 "MyConfig"
設定區段並套用數個 DataAnnotations
規則:
public class MyConfigOptions
{
public const string MyConfig = "MyConfig";
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
public string Key1 { get; set; }
[Range(0, 1000,
ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public int Key2 { get; set; }
public int Key3 { get; set; }
}
下列程式碼範例:
- 呼叫 AddOptions 以取得繫結至
MyConfigOptions
類別的 OptionsBuilder<TOptions>。 - 使用
DataAnnotations
來呼叫 ValidateDataAnnotations 以啟用驗證。
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions<MyConfigOptions>()
.Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations();
services.AddControllersWithViews();
}
ValidateDataAnnotations
擴充方法定義在 Microsoft.Extensions.Options.DataAnnotations NuGet 套件中。 對於使用 Microsoft.NET.Sdk.Web
SDK 的 Web 應用程式,此套件會從共用架構隱含參考。
下列程式碼會顯示設定值或驗證錯誤:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IOptions<MyConfigOptions> _config;
public HomeController(IOptions<MyConfigOptions> config,
ILogger<HomeController> logger)
{
_config = config;
_logger = logger;
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);
}
下列程式碼會使用委派來套用更複雜的驗證規則:
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions<MyConfigOptions>()
.Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Key2 != 0)
{
return config.Key3 > config.Key2;
}
return true;
}, "Key3 must be > than Key2."); // Failure message.
services.AddControllersWithViews();
}
用於複雜驗證的 IValidateOptions
下列類別會實作 IValidateOptions<TOptions>:
public class MyConfigValidation : IValidateOptions<MyConfigOptions>
{
public MyConfigOptions _config { get; private set; }
public MyConfigValidation(IConfiguration config)
{
_config = config.GetSection(MyConfigOptions.MyConfig)
.Get<MyConfigOptions>();
}
public ValidateOptionsResult Validate(string name, MyConfigOptions options)
{
string vor=null;
var rx = new Regex(@"^[a-zA-Z''-'\s]{1,40}$");
var match = rx.Match(options.Key1);
if (string.IsNullOrEmpty(match.Value))
{
vor = $"{options.Key1} doesn't match RegEx \n";
}
if ( options.Key2 < 0 || options.Key2 > 1000)
{
vor = $"{options.Key2} doesn't match Range 0 - 1000 \n";
}
if (_config.Key2 != default)
{
if(_config.Key3 <= _config.Key2)
{
vor += "Key3 must be > than Key2.";
}
}
if (vor != null)
{
return ValidateOptionsResult.Fail(vor);
}
return ValidateOptionsResult.Success;
}
}
IValidateOptions
可讓您將驗證碼移出 StartUp
並移至一個類別。
使用上述程式碼時,會使用下列程式碼在 Startup.ConfigureServices
中啟用驗證:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfigOptions>(Configuration.GetSection(
MyConfigOptions.MyConfig));
services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
<MyConfigOptions>, MyConfigValidation>());
services.AddControllersWithViews();
}
選項設定後作業
使用 IPostConfigureOptions<TOptions> 來設定設定後作業。 設定後作業會在所有 IConfigureOptions<TOptions> 設定發生後執行:
services.PostConfigure<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
PostConfigure 可用來後置設定具名選項:
services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
使用 PostConfigureAll 後置設定所有設定執行個體:
services.PostConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
在啟動期間存取選項
IOptions<TOptions> 與 IOptionsMonitor<TOptions> 可用於 Startup.Configure
,因為服務是在 Configure
方法執行之前建置。
public void Configure(IApplicationBuilder app,
IOptionsMonitor<MyOptions> optionsAccessor)
{
var option1 = optionsAccessor.CurrentValue.Option1;
}
請勿在 Startup.ConfigureServices
中使用 IOptions<TOptions> 或 IOptionsMonitor<TOptions>。 可能會因為服務註冊的順序而有不一致的選項狀態存在。
Options.ConfigurationExtensions NuGet 套件
Microsoft.Extensions.Options.ConfigurationExtensions 套件會在 ASP.NET Core 應用程式中隱含參考。