Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Note
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Warning
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Pola opsi menggunakan kelas untuk menyediakan akses yang sangat diketik ke grup pengaturan terkait. Saat setelan konfigurasi diisolasi berdasarkan skenario ke dalam kelas terpisah, aplikasi mematuhi dua prinsip rekayasa perangkat lunak penting:
- Enkapulasi: Kelas yang bergantung pada pengaturan konfigurasi hanya bergantung pada pengaturan konfigurasi yang mereka gunakan.
- Pemisahan Kekhawatiran: Pengaturan untuk berbagai bagian aplikasi tidak bergantung atau digabungkan satu sama lain.
Opsi juga menyediakan mekanisme untuk memvalidasi data konfigurasi, yang dijelaskan di bagian Validasi opsi .
Artikel ini menyediakan informasi tentang pola opsi di ASP.NET Core. Untuk informasi tentang menggunakan pola opsi di aplikasi konsol, lihat Pola opsi di .NET.
Contoh dalam artikel ini mengandalkan pemahaman umum tentang menyuntikkan layanan ke dalam kelas. Untuk informasi selengkapnya, lihat injeksi Dependensi di ASP.NET Core. Contoh didasarkan pada komponen Blazor dari Razor. Untuk melihat Razor contoh Halaman, lihat versi 7.0 dari artikel ini. Contoh dalam versi .NET 8 atau yang lebih baru dari artikel ini menggunakan konstruktor utama (Konstruktor Utama (Panduan C#)) dan jenis referensi nullable (NRT) dengan analisis statis keadaan-null oleh kompilator .NET.
Cara menggunakan pola opsi
Pertimbangkan data konfigurasi JSON berikut dari file pengaturan aplikasi (misalnya, appsettings.json), yang mencakup data terkait untuk nama dan judul karyawan dalam posisi organisasi:
"Position": {
"Name": "Joe Smith",
"Title": "Editor"
}
Kelas opsi berikut PositionOptions :
- Adalah POCO, kelas .NET sederhana dengan properti. Kelas opsi tidak boleh menjadi kelas abstrak.
- Memiliki properti baca-tulis publik yang cocok dengan entri terkait dalam data konfigurasi.
-
Tidak memiliki bidang (
Position) terikat. BidangPositionini digunakan untuk menghindari hardcoding string"Position"di aplikasi saat mengikat kelas ke penyedia konfigurasi.
PositionOptions.cs:
public class PositionOptions
{
public const string Position = "Position";
public string? Name { get; set; }
public string? Title { get; set; }
}
Contoh berikut:
- Panggilan ConfigurationBinder.Bind untuk mengaitkan kelas
PositionOptionske bagianPosition. - Menampilkan data konfigurasi
Position.
BasicOptions.razor:
@page "/basic-options"
@inject IConfiguration Config
Name: @positionOptions?.Name<br>
Title: @positionOptions?.Title
@code {
private PositionOptions? positionOptions;
protected override void OnInitialized()
{
positionOptions = new PositionOptions();
Config.GetSection(PositionOptions.Position).Bind(positionOptions);
}
}
BasicOptions.cshtml:
@page
@model RazorPagesSample.Pages.BasicOptionsModel
@{
}
BasicOptions.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesSample.Pages;
public class BasicOptionsModel : PageModel
{
private readonly IConfiguration _config;
public BasicOptionsModel(IConfiguration config)
{
_config = config;
}
public ContentResult OnGet()
{
var positionOptions = new PositionOptions();
_config.GetSection(PositionOptions.Position).Bind(positionOptions);
return Content(
$"Name: {positionOptions.Name}\n" +
$"Title: {positionOptions.Title}");
}
}
Keluaran:
Name: Joe Smith
Title: Editor
Setelah aplikasi dimulai, perubahan pada konfigurasi JSON dalam file pengaturan aplikasi dibaca. Untuk menunjukkan perilaku, ubah satu atau kedua nilai konfigurasi dalam file pengaturan aplikasi dan muat ulang halaman tanpa memulai ulang aplikasi.
Bind memungkinkan kelas abstrak untuk diinstansiasi. Pertimbangkan contoh berikut yang menggunakan kelas AbstractClassWithNameabstrak .
NameTitleOptions.cs:
public abstract class AbstractClassWithName
{
public abstract string? Name { get; set; }
}
public class NameTitleOptions(int age) : AbstractClassWithName
{
public const string NameTitle = "NameTitle";
public override string? Name { get; set; }
public string? Title { get; set; }
public int Age { get; set; } = age;
}
Konfigurasi JSON:
"NameTitle": {
"Name": "Sally Jones",
"Title": "Writer"
}
Contoh berikut menampilkan NameTitleOptions nilai konfigurasi.
AbstractClassOptions.razor:
@page "/abstract-class-options"
@inject IConfiguration Config
Name: @nameTitleOptions?.Name<br>
Title: @nameTitleOptions?.Title<br>
Age: @nameTitleOptions?.Age
@code {
private NameTitleOptions? nameTitleOptions;
protected override void OnInitialized()
{
nameTitleOptions = new NameTitleOptions(22);
Config.GetSection(NameTitleOptions.NameTitle).Bind(nameTitleOptions);
}
}
AbstractClassOptions.cshtml:
@page
@model RazorPagesSample.Pages.AbstractClassOptionsModel
@{
}
AbstractClassOptions.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesSample.Pages;
public class AbstractClassOptionsModel : PageModel
{
private readonly IConfiguration _config;
public AbstractClassOptionsModel(IConfiguration config)
{
_config = config;
}
public ContentResult OnGet()
{
var nameTitleOptions = new NameTitleOptions(22);
_config.GetSection(NameTitleOptions.NameTitle).Bind(nameTitleOptions);
return Content(
$"Name: {nameTitleOptions.Name}\n" +
$"Title: {nameTitleOptions.Title}\n" +
$"Age: {nameTitleOptions.Age}");
}
}
Keluaran:
Name: Sally Jones
Title: Writer
Age: 22
ConfigurationBinder.Get mengikat dan mengembalikan jenis yang ditentukan. Contoh berikut menunjukkan cara menggunakan Get dengan kelas PositionOptions.
GetOptions.razor:
@page "/get-options"
@inject IConfiguration Config
Name: @positionOptions?.Name<br>
Title: @positionOptions?.Title
@code {
private PositionOptions? positionOptions;
protected override void OnInitialized() =>
positionOptions = Config.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
}
GetOptions.cshtml:
@page
@model RazorPagesSample.Pages.GetOptionsModel
@{
}
GetOptions.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesSample.Pages;
public class GetOptionsModel : PageModel
{
private readonly IConfiguration _config;
public GetOptionsModel(IConfiguration config)
{
_config = config;
}
public ContentResult OnGet()
{
var positionOptions = _config.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
return Content(
$"Name: {positionOptions?.Name}\n" +
$"Title: {positionOptions?.Title}");
}
}
Keluaran:
Name: Joe Smith
Title: Editor
Setelah aplikasi dimulai, perubahan pada konfigurasi JSON dalam file pengaturan aplikasi dibaca. Untuk menunjukkan perilaku, ubah satu atau kedua nilai konfigurasi dalam file pengaturan aplikasi dan muat ulang halaman tanpa memulai ulang aplikasi.
Ringkasan perbedaan antara ConfigurationBinder.Bind dan ConfigurationBinder.Get:
- Get biasanya lebih nyaman daripada menggunakan Bind karena Get membuat dan mengembalikan instans baru objek, sementara Bind mengisi properti instans objek yang ada yang biasanya dibuat oleh baris kode lain.
- Bind memungkinkan konkresi dari kelas abstrak, sementara Get hanya dapat membuat instans non-abstrak dari jenis opsi.
Mengikat opsi ke kontainer layanan injeksi dependensi
Dalam contoh berikut, PositionOptions ditambahkan ke kontainer layanan dengan Configure dan terikat ke konfigurasi.
Konfigurasi JSON:
"Position": {
"Name": "Joe Smith",
"Title": "Editor"
}
PositionOptions.cs:
public class PositionOptions
{
public const string Position = "Position";
public string? Name { get; set; }
public string? Title { get; set; }
}
Di mana layanan terdaftar untuk injeksi dependensi dalam file aplikasi Program :
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
Contoh berikut ini memeriksa opsi posisi.
DIOptions.razor:
@page "/di-options"
@using Microsoft.Extensions.Options
@inject IOptions<PositionOptions> Options
Name: @Options.Value.Name<br>
Title: @Options.Value.Title
DIOptions.cshtml:
@page
@model RazorPagesSample.Pages.DIOptionsModel
@{
}
DIOptions.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
namespace RazorPagesSample.Pages;
public class DIOptionsModel : PageModel
{
private readonly IOptions<PositionOptions> _options;
public DIOptionsModel(IOptions<PositionOptions> options)
{
_options = options;
}
public ContentResult OnGet()
{
return Content(
$"Name: {_options.Value.Name}\n" +
$"Title: {_options.Value.Title}");
}
}
Keluaran:
Name: Joe Smith
Title: Editor
Untuk kode sebelumnya, perubahan pada konfigurasi JSON dalam file pengaturan aplikasi setelah aplikasi dimulai tidak dibaca. Untuk membaca perubahan setelah aplikasi dimulai, gunakan IOptionsSnapshot.
Antarmuka opsi
- Tidak mendukung:
- Membaca data konfigurasi setelah aplikasi dimulai.
- Opsi bernama.
- Terdaftar sebagai layanan singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Dibahas kemudian dalam artikel ini di bagian Gunakan
IOptionsSnapshotuntuk membaca data yang diperbarui . - Berguna dalam skenario di mana opsi harus dikomputasi ulang pada setiap permintaan.
- Terdaftar sebagai layanan terlingkup, sehingga tidak dapat disuntikkan ke dalam layanan singleton.
- Mendukung opsi yang diberi nama .
- Dibahas kemudian dalam artikel ini di bagian Gunakan
IOptionsMonitoruntuk membaca data yang diperbarui . - Digunakan untuk mengambil opsi dan mengelola pemberitahuan opsi untuk
TOptionsinstance. - Terdaftar sebagai layanan singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Supports:
- Mengubah pemberitahuan.
- Opsi bernama.
- Konfigurasi yang dapat dimuat ulang.
- Pembatalan pilihan selektif (IOptionsMonitorCache<TOptions>).
Skenario pasca-konfigurasi mengaktifkan pengaturan atau mengubah opsi setelah semua IConfigureOptions<TOptions> konfigurasi terjadi.
IOptionsFactory<TOptions> bertanggung jawab untuk membuat instans opsi baru. Ini memiliki satu metode Create. Implementasi default mengambil semua IConfigureOptions<TOptions> dan IPostConfigureOptions<TOptions> yang terdaftar dan menjalankan semua konfigurasi terlebih dahulu, kemudian diikuti oleh konfigurasi lanjutan. Ini membedakan antara IConfigureNamedOptions<TOptions> dan IConfigureOptions<TOptions> dan hanya memanggil antarmuka yang sesuai.
Gunakan IOptionsSnapshot untuk membaca data yang diperbarui
Menggunakan IOptionsSnapshot<TOptions>:
- Opsi dihitung sekali per permintaan saat diakses dan di-cache selama masa pakai permintaan.
- Dapat dikenakan penalti performa yang signifikan karena merupakan layanan tercakup dan dikomputasi ulang per permintaan. Untuk informasi selengkapnya, lihat
IOptionsSnapshotsangat lambat (dotnet/runtime#53793) dan Meningkatkan performa pengikatan konfigurasi (dotnet/runtime#36130). - Perubahan pada konfigurasi dibaca setelah aplikasi dimulai saat menggunakan penyedia konfigurasi yang mendukung pembacaan nilai konfigurasi yang diperbarui.
Perbedaan antara IOptionsMonitor dan IOptionsSnapshot<TOptions> adalah bahwa:
- IOptionsMonitor<TOptions> adalah layanan singleton yang mengambil nilai opsi saat ini kapan saja, yang terutama berguna dalam dependensi singleton.
-
IOptionsSnapshot<TOptions> adalah layanan lingkup dan menyediakan cuplikan opsi pada saat objek
IOptionsSnapshot<T>dibangun. Cuplikan opsi dirancang untuk digunakan dengan dependensi transien dan dengan lingkup tertentu.
Runtime ASP.NET Core menggunakan OptionsCache<TOptions> untuk menyimpan instans opsi setelah dibuat.
Konfigurasi JSON:
"Position": {
"Name": "Joe Smith",
"Title": "Editor"
}
PositionOptions.cs:
public class PositionOptions
{
public const string Position = "Position";
public string? Name { get; set; }
public string? Title { get; set; }
}
Contoh berikut menggunakan IOptionsSnapshot<TOptions>.
SnapshotOptions.razor:
@page "/snapshot-options"
@using Microsoft.Extensions.Options
@inject IOptionsSnapshot<PositionOptions> Options
Name: @Options.Value.Name<br>
Title: @Options.Value.Title
SnapshotOptions.cshtml:
@page
@model RazorPagesSample.Pages.SnapshotOptionsModel
@{
}
SnapshotOptions.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
namespace RazorPagesSample.Pages;
public class SnapshotOptionsModel : PageModel
{
private readonly IOptionsSnapshot<PositionOptions> _options;
public SnapshotOptionsModel(IOptionsSnapshot<PositionOptions> options)
{
_options = options;
}
public ContentResult OnGet()
{
return Content(
$"Name: {_options.Value.Name}\n" +
$"Title: {_options.Value.Title}");
}
}
Dalam konteks di mana layanan didaftarkan untuk injeksi dependensi, contoh berikut ini mendaftarkan instance konfigurasi yang PositionOptions terikat pada:
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
Keluaran:
Name: Joe Smith
Title: Editor
Setelah aplikasi dimulai, perubahan pada konfigurasi JSON dalam file pengaturan aplikasi dibaca. Untuk menunjukkan perilaku, ubah satu atau kedua nilai konfigurasi dalam file pengaturan aplikasi dan muat ulang halaman tanpa memulai ulang aplikasi.
Gunakan IOptionsMonitor untuk membaca data yang diperbarui
IOptionsMonitor<TOptions> digunakan untuk mengambil opsi dan mengelola pemberitahuan opsi untuk instance TOptions.
Perbedaan antara IOptionsMonitor<TOptions> dan IOptionsSnapshot adalah bahwa:
- IOptionsMonitor<TOptions> adalah layanan singleton yang mengambil nilai opsi saat ini kapan saja, yang terutama berguna dalam dependensi singleton.
-
IOptionsSnapshot<TOptions> adalah layanan lingkup dan menyediakan cuplikan opsi pada saat objek
IOptionsSnapshot<T>dibangun. Cuplikan opsi dirancang untuk digunakan dengan dependensi transien dan dengan lingkup tertentu.
IOptionsMonitorCache<TOptions> digunakan oleh IOptionsMonitor<TOptions> untuk menyimpan TOptions instance.
IOptionsMonitorCache<TOptions>.TryRemove membatalkan instans opsi di monitor sehingga nilai dikomputasi ulang. Nilai dapat diperkenalkan secara manual dengan IOptionsMonitorCache<TOptions>.TryAdd. Metode Clear ini digunakan ketika semua instans bernama harus dibuat ulang sesuai permintaan.
Konfigurasi JSON:
"Position": {
"Name": "Joe Smith",
"Title": "Editor"
}
PositionOptions.cs:
public class PositionOptions
{
public const string Position = "Position";
public string? Name { get; set; }
public string? Title { get; set; }
}
Dalam konteks di mana layanan didaftarkan untuk injeksi dependensi, contoh berikut ini mendaftarkan instance konfigurasi yang PositionOptions terikat pada:
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
Contoh berikut menggunakan IOptionsMonitor<TOptions>.
MonitorOptions.razor:
@page "/monitor-options"
@using Microsoft.Extensions.Options
@inject IOptionsMonitor<PositionOptions> Options
Name: @Options.CurrentValue.Name<br>
Title: @Options.CurrentValue.Title
MonitorOptions.cshtml:
@page
@model RazorPagesSample.Pages.MonitorOptionsModel
@{
}
MonitorOptions.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
namespace RazorPagesSample.Pages;
public class MonitorOptionsModel : PageModel
{
private readonly IOptionsMonitor<PositionOptions> _options;
public MonitorOptionsModel(IOptionsMonitor<PositionOptions> options)
{
_options = options;
}
public ContentResult OnGet()
{
return Content(
$"Name: {_options.CurrentValue.Name}\n" +
$"Title: {_options.CurrentValue.Title}");
}
}
Keluaran:
Name: Joe Smith
Title: Editor
Setelah aplikasi dimulai, perubahan pada konfigurasi JSON dalam file pengaturan aplikasi dibaca. Untuk menunjukkan perilaku, ubah satu atau kedua nilai konfigurasi dalam file pengaturan aplikasi dan muat ulang halaman tanpa memulai ulang aplikasi.
Tentukan nama kunci kustom untuk properti konfigurasi menggunakan ConfigurationKeyName
Secara default, nama properti kelas options digunakan sebagai nama kunci dalam sumber konfigurasi. Jika nama properti adalah Title, nama kunci dalam konfigurasi Title juga.
Saat nama diferensiasi, Anda dapat menggunakan [ConfigurationKeyName] atribut untuk menentukan nama kunci dalam sumber konfigurasi. Dengan menggunakan teknik ini, Anda dapat memetakan properti dalam konfigurasi ke properti dalam kode Anda dengan nama yang berbeda. Ini berguna ketika nama properti dalam sumber konfigurasi bukan pengidentifikasi C# yang valid atau saat Anda ingin menggunakan nama yang berbeda dalam kode Anda.
Misalnya, pertimbangkan kelas opsi berikut.
PositionKeyName.cs:
public class PositionKeyName
{
public const string Position = "PositionKeyName";
[ConfigurationKeyName("PositionName")]
public string? Name { get; set; }
[ConfigurationKeyName("PositionTitle")]
public string? Title { get; set; }
}
Properti Name kelas dan Title terikat ke PositionName dan PositionTitle dari konfigurasi JSON berikut:
"PositionKeyName": {
"PositionName": "Carlos Diego",
"PositionTitle": "Director"
}
PositionKeyNameOptions.razor:
@page "/position-key-name-options"
@inject IConfiguration Config
Name: @positionOptions?.Name<br>
Title: @positionOptions?.Title
@code {
private PositionKeyName? positionOptions;
protected override void OnInitialized() =>
positionOptions = Config.GetSection(PositionKeyName.Position)
.Get<PositionKeyName>();
}
PositionKeyName.cshtml:
@page
@model RazorPagesSample.Pages.PositionKeyNameModel
@{
}
PositionKeyName.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorPagesSample.Pages;
public class PositionKeyNameModel : PageModel
{
private readonly IConfiguration _config;
public PositionKeyNameModel(IConfiguration config)
{
_config = config;
}
public ContentResult OnGet()
{
var positionOptions = _config.GetSection(PositionKeyName.Position)
.Get<PositionKeyName>();
return Content(
$"Name: {positionOptions?.Name}\n" +
$"Title: {positionOptions?.Title}");
}
}
Keluaran:
Name: Carlos Diego
Title: Director
Setelah aplikasi dimulai, perubahan pada konfigurasi JSON dalam file pengaturan aplikasi dibaca. Untuk menunjukkan perilaku, ubah satu atau kedua nilai konfigurasi dalam file pengaturan aplikasi dan muat ulang halaman tanpa memulai ulang aplikasi.
Dukungan opsi bernama menggunakan IConfigureNamedOptions
Opsi bernama:
- Berguna saat beberapa bagian konfigurasi mengikat properti yang sama.
- Peka huruf besar/kecil.
Pertimbangkan konfigurasi JSON berikut:
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
Daripada membuat dua kelas untuk mengikat TopItem:Month dan TopItem:Year, kelas berikut digunakan untuk setiap bagian.
TopItemSettings.cs:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string? Name { get; set; }
public string? Model { get; set; }
}
Ketika layanan terdaftar untuk injeksi dependensi, contoh berikut mengonfigurasi opsi yang diberi nama:
builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
Contoh berikut menampilkan opsi-opsi yang disebutkan:
NamedOptions.razor:
@page "/named-options"
@using Microsoft.Extensions.Options
@inject IOptionsSnapshot<TopItemSettings> Options
Month: Name: @monthTopItem?.Name Model: @monthTopItem?.Model<br>
Year: Name: @yearTopItem?.Name Model: @yearTopItem?.Model
@code {
private TopItemSettings? monthTopItem;
private TopItemSettings? yearTopItem;
protected override void OnInitialized()
{
monthTopItem = Options.Get(TopItemSettings.Month);
yearTopItem = Options.Get(TopItemSettings.Year);
}
}
NamedOptions.cshtml:
@page
@model RazorPagesSample.Pages.NamedOptionsModel
@{
}
NamedOptions.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
namespace RazorPagesSample.Pages;
public class NamedOptionsModel : PageModel
{
private readonly IOptionsSnapshot<TopItemSettings> _options;
public NamedOptionsModel(IOptionsSnapshot<TopItemSettings> options)
{
_options = options;
}
public ContentResult OnGet()
{
var monthTopItem = _options.Get(TopItemSettings.Month);
var yearTopItem = _options.Get(TopItemSettings.Year);
return Content(
$"Month:Name {monthTopItem.Name}\n" +
$"Month:Model {monthTopItem.Model}\n" +
$"Year:Name {yearTopItem.Name}\n" +
$"Year:Model {yearTopItem.Model}");
}
}
Semua opsi diberi nama instans.
IConfigureOptions<TOptions> instans diperlakukan sebagai penargetan kepada instans Options.DefaultName, yaitu string.Empty.
IConfigureNamedOptions<TOptions> juga mengimplementasikan IConfigureOptions<TOptions>. Implementasi default dari IOptionsFactory<TOptions> memiliki logika untuk menggunakan masing-masing dengan tepat.
null Opsi bernama digunakan untuk menargetkan semua instans bernama alih-alih instans bernama tertentu.
ConfigureAll dan PostConfigureAll gunakan konvensi ini.
Panduan tentang pasca-konfigurasi opsi bernama disediakan di bagian Opsi pasca-konfigurasi .
OptionsBuilder Antarmuka Pemrograman Aplikasi (API)
OptionsBuilder<TOptions> digunakan untuk mengonfigurasi TOptions instance.
OptionsBuilder menyederhanakan pembuatan opsi bernama karena ini hanya membutuhkan satu parameter ke panggilan awal AddOptions<TOptions>(string optionsName) dan tidak perlu muncul di semua panggilan berikutnya. Validasi opsi dan IConfigureOptions<TOptions> kelebihan beban yang menerima dependensi layanan hanya tersedia melalui OptionsBuilder (lihat Menggunakan layanan DI untuk mengonfigurasi opsi).
OptionsBuilder<TOptions> ditunjukkan di bagian Validasi opsi .
Untuk informasi menambahkan repositori kustom, lihat Mulai menggunakan API Perlindungan Data di ASP.NET Core.
Menggunakan layanan DI untuk mengonfigurasi opsi
Layanan dapat diakses dari injeksi dependensi sambil mengonfigurasi opsi dengan dua cara:
Pendekatan delegasi konfigurasi
Di mana layanan terdaftar untuk injeksi dependensi, teruskan delegasi konfigurasi ke Configure pada OptionsBuilder<TOptions>.
OptionsBuilder<TOptions> menyediakan kelebihan beban Configure yang memungkinkan penggunaan hingga lima layanan untuk mengonfigurasi opsi:
builder.Services.AddOptions<PositionOptions>("optionalName")
.Configure<Service1, Service2, Service3, Service4, Service5>(
(o, s, s2, s3, s4, s5) =>
o.Property = DoSomethingWith(s, s2, s3, s4, s5));
services.AddOptions<PositionOptions>("optionalName")
.Configure<Service1, Service2, Service3, Service4, Service5>(
(o, s, s2, s3, s4, s5) =>
o.Property = DoSomethingWith(s, s2, s3, s4, s5));
Pendekatan layanan opsi konfigurasi
Buat jenis yang mengimplementasikan IConfigureOptions<TOptions> atau IConfigureNamedOptions<TOptions> dan mendaftarkan jenis sebagai layanan.
Kami merekomendasikan untuk meneruskan configuration delegate ke Configure, karena proses penciptaan layanan lebih kompleks. Membuat tipe setara dengan apa yang dilakukan kerangka kerja saat memanggil Configure. Pemanggilan Configure mendaftarkan generik sementara IConfigureNamedOptions<TOptions>, yang memiliki konstruktor yang dapat menerima jenis layanan generik yang telah ditentukan.
Validasi opsi
Validasi opsi memungkinkan validasi nilai opsi.
Pertimbangkan konfigurasi JSON berikut:
"KeyOptions": {
"Key1": "Key One",
"Key2": 10,
"Key3": 32
}
Kelas berikut digunakan untuk mengikat ke "KeyOptions" bagian konfigurasi dan menerapkan dua aturan anotasi data, yang mencakup persyaratan ekspresi dan rentang reguler.
KeyOptions.cs:
public class KeyOptions
{
public const string Key = "KeyOptions";
[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; }
}
Ketika layanan didaftarkan untuk injeksi dependensi, berikut adalah contohnya:
- Panggilan AddOptions untuk mendapatkan OptionsBuilder<TOptions> yang terhubung ke kelas
KeyOptions. - Memanggil ValidateDataAnnotations untuk mengaktifkan validasi.
builder.Services.AddOptions<KeyOptions>()
.Bind(builder.Configuration.GetSection(KeyOptions.Key))
.ValidateDataAnnotations();
services.AddOptions<KeyOptions>()
.Bind(builder.Configuration.GetSection(KeyOptions.Key))
.ValidateDataAnnotations();
Metode ValidateDataAnnotations ekstensi didefinisikan dalam Microsoft.Extensions.Options.DataAnnotations paket NuGet. Untuk aplikasi web yang menggunakan Microsoft.NET.Sdk.Web SDK, paket ini dirujuk secara implisit dari kerangka kerja bersama.
Di mana layanan terdaftar untuk injeksi dependensi, contoh berikut menerapkan aturan validasi yang lebih kompleks menggunakan delegasi:
builder.Services.AddOptions<KeyOptions>()
.Bind(builder.Configuration.GetSection(KeyOptions.Key))
.ValidateDataAnnotations()
.Validate(options =>
{
return options.Key3 > options.Key2;
}, "Key3 must be > than Key2");
services.AddOptions<KeyOptions>()
.Bind(builder.Configuration.GetSection(KeyOptions.Key))
.ValidateDataAnnotations()
.Validate(options =>
{
return options.Key3 > options.Key2;
}, "Key3 must be > than Key2");
Contoh berikut menunjukkan cara mencatat pengecualian validasi opsi dan menampilkan OptionsValidationException.Message.
Note
Untuk tujuan demonstrasi, contoh berikut menggunakan MarkupString untuk memformat HTML mentah. Perenderan HTML mentah yang dibuat dari sumber yang tidak tepercaya merupakan risiko keamanan dan harus selalu dihindari. Untuk informasi selengkapnya, lihat komponen ASP.NET CoreRazor.
OptionsValidation1.razor:
@page "/options-validation-1"
@inject IOptionsSnapshot<KeyOptions> Options
@inject ILogger<OptionsValidation1> Logger
@if (message is not null)
{
@((MarkupString)message)
}
@code {
private string? message;
protected override void OnInitialized()
{
try
{
var keyOptions = Options.Value;
}
catch (OptionsValidationException ex)
{
foreach (var failure in ex.Failures)
{
Logger.LogError(failure);
}
}
try
{
message =
$"Key1: {Options.Value.Key1}<br>" +
$"Key2: {Options.Value.Key2}<br>" +
$"Key3: {Options.Value.Key3}";
}
catch (OptionsValidationException optValEx)
{
message = optValEx.Message;
}
}
}
Contoh berikut menunjukkan cara mencatat pengecualian validasi opsi di konstruktor model halaman dan menampilkan OptionsValidationException.Message dalam metode halaman OnGet .
OptionsValidation1.cshtml:
@page
@model RazorPagesSample.Pages.OptionsValidation1Model
@{
}
OptionsValidation1.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
namespace RazorPagesSample.Pages;
public class OptionsValidation1Model : PageModel
{
private readonly IOptionsSnapshot<KeyOptions>? _options;
public OptionsValidation1Model(IOptionsSnapshot<KeyOptions> options,
ILogger<OptionsValidation1Model> logger)
{
_options = options;
try
{
var keyOptions = _options?.Value;
}
catch (OptionsValidationException ex)
{
foreach (var failure in ex.Failures)
{
logger?.LogError("Validation: {Failure}", failure);
}
}
}
public ContentResult OnGet()
{
string message;
try
{
message =
$"Key1: {_options?.Value.Key1}\n" +
$"Key2: {_options?.Value.Key2}\n" +
$"Key3: {_options?.Value.Key3}";
}
catch (OptionsValidationException optValEx)
{
return Content(optValEx.Message);
}
return Content(message);
}
}
Kode sebelumnya menampilkan nilai konfigurasi atau kesalahan validasi:
- Gunakan kode dengan konfigurasi pengaturan aplikasi sebelumnya untuk menunjukkan tidak ada kesalahan validasi.
- Ubah konfigurasi dalam file pengaturan aplikasi dengan satu atau beberapa cara yang melanggar aturan anotasi data. Muat ulang halaman validasi opsi untuk melihat pelanggaran aturan.
Dalam contoh berikut, konten halaman menunjukkan bahwa nilai berada di luar rentang jika nilai Key2 dalam file pengaturan aplikasi diubah ke nilai di bawah nol atau lebih dari Key2 1.000:
DataAnnotation validation failed for 'KeyOptions' members: 'Key2' with the error: 'Value for Key2 must be between 0 and 1000.'.
Memvalidasi opsi di kelas khusus dengan IValidateOptions<TOptions>
Terapkan IValidateOptions<TOptions> untuk memvalidasi opsi tanpa perlu mempertahankan aturan validasi dengan anotasi data atau dalam file aplikasi Program .
Dalam contoh berikut, aturan dan opsi anotasi data mendelegasikan validasi contoh sebelumnya dipindahkan ke kelas validasi. Kelas model Opsi (KeyOptions2) tidak berisi anotasi data.
Pertimbangkan konfigurasi JSON berikut:
"KeyOptions": {
"Key1": "Key One",
"Key2": 10,
"Key3": 32
}
KeyOptions2.cs:
public class KeyOptions2
{
public const string Key = "KeyOptions";
public string? Key1 { get; set; }
public int Key2 { get; set; }
public int Key3 { get; set; }
}
public class KeyOptionsValidation : IValidateOptions<KeyOptions2>
{
public ValidateOptionsResult Validate(string? name, KeyOptions2 options)
{
if (options == null)
{
return ValidateOptionsResult.Fail("KeyOptions not found.");
}
StringBuilder? validationResult = new();
var rx = new Regex(@"^[a-zA-Z\s]{1,40}$");
var match = rx.Match(options.Key1!);
if (string.IsNullOrEmpty(match.Value))
{
validationResult.Append($"{options.Key1} doesn't match RegEx<br>");
}
if (options.Key2 < 0 || options.Key2 > 1000)
{
validationResult.Append($"{options.Key2} doesn't match Range 0 - 1000<br>");
}
if (options.Key3 < options.Key2)
{
validationResult.Append("Key3 must be > than Key2<br>");
}
if (validationResult.Length > 0)
{
return ValidateOptionsResult.Fail(validationResult.ToString());
}
return ValidateOptionsResult.Success;
}
}
public class KeyOptionsValidation : IValidateOptions<KeyOptions2>
{
public ValidateOptionsResult Validate(string? name, KeyOptions2 options)
{
if (options == null)
{
return ValidateOptionsResult.Fail("KeyOptions not found");
}
StringBuilder? validationResult = new();
var rx = new Regex(@"^[a-zA-Z\s]{1,40}$");
var match = rx.Match(options.Key1!);
if (string.IsNullOrEmpty(match.Value))
{
validationResult.Append($"{options.Key1} doesn't match RegEx\n");
}
if (options.Key2 < 0 || options.Key2 > 1000)
{
validationResult.Append($"{options.Key2} doesn't match Range 0 - 1000\n");
}
if (options.Key3 < options.Key2)
{
validationResult.Append("Key3 must be > than Key2\n");
}
if (validationResult.Length > 0)
{
return ValidateOptionsResult.Fail(validationResult.ToString());
}
return ValidateOptionsResult.Success;
}
}
Di mana layanan terdaftar untuk injeksi dependensi dan menggunakan kode sebelumnya, validasi diaktifkan dalam Program.cs dengan contoh berikut:
builder.Services.Configure<KeyOptions2>(
builder.Configuration.GetSection(KeyOptions2.Key));
builder.Services.AddSingleton<IValidateOptions<KeyOptions2>,
KeyOptionsValidation>();
services.Configure<KeyOptions>(
builder.Configuration.GetSection(KeyOptions2.Key));
services.AddSingleton<IValidateOptions<KeyOptions2>, KeyOptionsValidation>();
Contoh berikut menunjukkan cara mencatat pengecualian validasi opsi dan menampilkan OptionsValidationException.Message.
OptionsValidation2.razor:
@page "/options-validation-2"
@inject IOptionsSnapshot<KeyOptions2> Options
@inject ILogger<OptionsValidation2> Logger
@if (message is not null)
{
@((MarkupString)message)
}
@code {
private string? message;
protected override void OnInitialized()
{
try
{
var keyOptions = Options.Value;
}
catch (OptionsValidationException ex)
{
foreach (var failure in ex.Failures)
{
Logger.LogError(failure);
}
}
try
{
message =
$"Key1: {Options.Value.Key1}<br>" +
$"Key2: {Options.Value.Key2}<br>" +
$"Key3: {Options.Value.Key3}";
}
catch (OptionsValidationException optValEx)
{
message = optValEx.Message;
}
}
}
Contoh berikut menunjukkan cara mencatat pengecualian validasi opsi di konstruktor model halaman dan menampilkan OptionsValidationException.Message dalam metode halaman OnGet .
OptionsValidation2.cshtml:
@page
@model RazorPagesSample.Pages.OptionsValidation2Model
@{
}
OptionsValidation2.cshtml.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
namespace RazorPagesSample.Pages;
public class OptionsValidation2Model : PageModel
{
private readonly IOptionsSnapshot<KeyOptions2>? _options;
public OptionsValidation2Model(IOptionsSnapshot<KeyOptions2> options,
ILogger<OptionsValidation2Model> logger)
{
_options = options;
try
{
var keyOptions = _options?.Value;
}
catch (OptionsValidationException ex)
{
foreach (var failure in ex.Failures)
{
logger?.LogError("Validation: {Failure}", failure);
}
}
}
public ContentResult OnGet()
{
string message;
try
{
message =
$"Key1: {_options?.Value.Key1}\n" +
$"Key2: {_options?.Value.Key2}\n" +
$"Key3: {_options?.Value.Key3}";
}
catch (OptionsValidationException optValEx)
{
return Content(optValEx.Message);
}
return Content(message);
}
}
Validasi tingkat kelas dengan IValidatableObject
Validasi opsi mendukung IValidatableObject untuk melakukan validasi tingkat kelas kelas dalam kelas:
- Implementasikan antarmuka IValidatableObject dan metodenya Validate di dalam kelas.
- Panggil ValidateDataAnnotations dalam
Programfile.
Jalankan validasi opsi saat aplikasi dimulai dengan ValidateOnStart
Validasi opsi berjalan saat pertama kali TOption instans dibuat, yaitu ketika akses IOptionsSnapshot<TOptions>.Value pertama terjadi dalam alur permintaan atau ketika IOptionsMonitor<TOptions>.Get(string) dipanggil. Setiap kali opsi dimuat ulang, validasi berjalan lagi.
Untuk menjalankan validasi opsi saat aplikasi dimulai, panggil ValidateOnStart di dalam file Program di mana service terdaftar untuk injeksi dependensi:
builder.Services.AddOptions<KeyOptions>()
.Bind(builder.Configuration.GetSection(KeyOptions.Key))
.ValidateDataAnnotations()
.ValidateOnStart();
Opsi setelah konfigurasi
Di mana layanan terdaftar untuk injeksi dependensi, PostConfigure tersedia untuk menginisialisasi opsi bernama tertentu. Dalam contoh berikut, hanya TopItem:Month:Name yang dikustomisasi setelah pengaturan awal.
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>(TopItemSettings.Month, options =>
{
options.Name = "Blue Gizmo";
});
services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
services.PostConfigure<TopItemSettings>(TopItemSettings.Month, options =>
{
options.Name = "Blue Gizmo";
});
Output yang dirender dari contoh opsi bernama, di mana hanya TopItem:Month:Name yang dikonfigurasi setelahnya:
Month: Name: Blue Gizmo Model: GW46
Year: Name: Orange Gadget Model: OG35
Di mana layanan terdaftar untuk injeksi dependensi, gunakan PostConfigureAll untuk menginisialisasi semua instans bernama dari jenis opsi yang ditentukan. Dalam contoh berikut, semua instans TopItem.Name diatur ke Blue Gizmo:
builder.Services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
builder.Services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
builder.Services.PostConfigureAll<TopItemSettings>(options =>
{
options.Name = "Blue Gizmo";
});
services.Configure<TopItemSettings>(TopItemSettings.Month,
builder.Configuration.GetSection("TopItem:Month"));
services.Configure<TopItemSettings>(TopItemSettings.Year,
builder.Configuration.GetSection("TopItem:Year"));
services.PostConfigureAll<TopItemSettings>(options =>
{
options.Name = "Blue Gizmo";
});
Output yang dirender dari contoh opsi bernama, di mana semua TopItem:Name opsi sudah dikonfigurasi:
Month: Name: Blue Gizmo Model: GW46
Year: Name: Blue Gizmo Model: OG35
Opsi akses dalam alur pemrosesan permintaan
Untuk mengakses IOptions<TOptions> atau IOptionsMonitor<TOptions> dalam alur pemrosesan permintaan, panggil GetRequiredService di WebApplication.Services:
var name = app.Services.GetRequiredService<IOptionsMonitor<PositionOptions>>()
.CurrentValue.Name;
IOptions<TOptions> dan IOptionsMonitor<TOptions> dapat digunakan dalam Startup.Configure, karena layanan dibangun sebelum Configure metode dijalankan.
Dalam contoh berikut, IOptionsMonitor<TOptions> disuntikkan ke dalam metode Startup.Configure untuk mendapatkan nilai PositionOptions.
public void Configure(IApplicationBuilder app,
IOptionsMonitor<PositionOptions> options)
Akses opsi dalam alur pemrosesan permintaan dari Startup.Configure:
var name = options.CurrentValue.Name;
Jangan gunakan IOptions<TOptions> atau IOptionsMonitor<TOptions> di Startup.ConfigureServices. Keadaan opsi yang tidak konsisten mungkin ada karena urutan pendaftaran layanan.
Sumber daya tambahan
ASP.NET Core