Pola opsi ASP.NET Core
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Peringatan
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 8 dari artikel ini.
Penting
Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.
Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.
Oleh Rick Anderson.
Pola opsi menggunakan kelas untuk menyediakan akses yang sangat diketik ke grup pengaturan terkait. Saat pengaturan konfigurasi diisolasi oleh 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. Untuk informasi selengkapnya, lihat 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.
Mengikat konfigurasi hierarkis
Cara yang lebih disukai untuk membaca nilai konfigurasi terkait adalah menggunakan pola opsi. Misalnya, untuk membaca nilai konfigurasi berikut:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Buat kelas PositionOptions
berikut:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
Kelas opsi:
- Harus non-abstrak.
- Memiliki properti baca-tulis publik dari jenis yang memiliki item yang sesuai dalam konfigurasi terikat.
- Memiliki properti baca-tulis yang terikat dengan entri yang cocok dalam konfigurasi.
- Tidak memiliki bidang yang terikat. Dalam kode sebelumnya,
Position
tidak terikat. BidangPosition
digunakan sehingga string"Position"
tidak perlu dikodekan secara permanen di aplikasi saat mengikat kelas ke penyedia konfigurasi.
Kode berikut:
- Memanggil ConfigurationBinder.Bind untuk mengikat kelas
PositionOptions
ke bagianPosition
. - Menampilkan data konfigurasi
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}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
ConfigurationBinder.Get<T>
mengikat dan mengembalikan jenis yang ditentukan. ConfigurationBinder.Get<T>
mungkin lebih nyaman daripada menggunakan ConfigurationBinder.Bind
. Contoh kode berikut menunjukkan cara menggunakan ConfigurationBinder.Get<T>
dengan kelas PositionOptions
:
public class Test21Model : PageModel
{
private readonly IConfiguration Configuration;
public PositionOptions? positionOptions { get; private set; }
public Test21Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
positionOptions = Configuration.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
Ikatan juga memungkinkan kesimpulan kelas abstrak. Pertimbangkan kode berikut yang menggunakan kelas SomethingWithAName
abstrak :
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;
}
Kode berikut menampilkan NameTitleOptions
nilai konfigurasi:
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}"
);
}
}
Panggilan ke Bind
kurang ketat daripada panggilan ke Get<>
:
Bind
memungkinkan kesimpulan abstrak.Get<>
harus membuat instans itu sendiri.
Pola Opsi
Pendekatan alternatif saat menggunakan pola opsi adalah untuk mengikat bagian Position
dan menambahkannya ke kontainer layanan injeksi dependensi. Dalam kode berikut, PositionOptions
ditambahkan ke kontainer layanan dengan Configure dan terikat ke konfigurasi:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
Dengan menggunakan kode sebelumnya, kode berikut membaca opsi posisi:
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}");
}
}
Dalam kode sebelumnya, perubahan pada file konfigurasi JSON 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 Singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Berguna dalam skenario di mana opsi harus dikomputasi ulang pada setiap permintaan. Untuk informasi selengkapnya, lihat Menggunakan IOptionsSnapshot untuk membaca data yang diperbarui.
- Terdaftar sebagai Cakupan dan oleh karena itu tidak dapat disuntikkan ke dalam layanan Singleton.
- Mendukung opsi bernama
- Digunakan untuk mengambil opsi dan mengelola pemberitahuan opsi untuk
TOptions
instans. - Terdaftar sebagai Singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Mendukung:
- Mengubah pemberitahuan
- opsi bernama
- Konfigurasi yang dapat dimuat ulang
- Invalidasi opsi 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 Create metode. Implementasi default mengambil semua yang terdaftar IConfigureOptions<TOptions> dan IPostConfigureOptions<TOptions> dan menjalankan semua konfigurasi terlebih dahulu, diikuti oleh pasca-konfigurasi. Ini membedakan antara IConfigureNamedOptions<TOptions> dan IConfigureOptions<TOptions> dan hanya memanggil antarmuka yang sesuai.
IOptionsMonitorCache<TOptions> digunakan oleh IOptionsMonitor<TOptions> untuk menyimpan TOptions
instans. IOptionsMonitorCache<TOptions> Instans opsi yang tidak valid di monitor sehingga nilai dikomputasi ulang (TryRemove). Nilai dapat diperkenalkan secara manual dengan TryAdd. Metode Clear ini digunakan ketika semua instans bernama harus dibuat ulang sesuai permintaan.
Menggunakan 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 Cakupan dan dikomputasi ulang per permintaan. Untuk informasi selengkapnya, lihat masalah GitHub ini dan Meningkatkan performa pengikatan konfigurasi.
- Perubahan pada konfigurasi dibaca setelah aplikasi dimulai saat menggunakan penyedia konfigurasi yang mendukung pembacaan nilai konfigurasi yang diperbarui.
Perbedaan antara IOptionsMonitor
dan IOptionsSnapshot
adalah bahwa:
IOptionsMonitor
adalah layanan Singleton yang mengambil nilai opsi saat ini kapan saja, yang sangat berguna dalam dependensi singleton.IOptionsSnapshot
adalah layanan Cakupan dan menyediakan rekam jepret opsi pada saatIOptionsSnapshot<T>
objek dibuat. Rekam jepret opsi dirancang untuk digunakan dengan dependensi sementara dan terlingkup.
Kode berikut menggunakan 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}");
}
}
Kode berikut mendaftarkan instans konfigurasi yang MyOptions
mengikat terhadap:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
Dalam kode sebelumnya, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
IOptionsMonitor
Kode berikut mendaftarkan instans konfigurasi yang MyOptions
mengikat.
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
Contoh berikut menggunakan 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}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
Dukungan opsi bernama menggunakan IConfigureNamedOptions
Opsi bernama:
- Berguna saat beberapa bagian konfigurasi mengikat properti yang sama.
- Peka huruf besar/kecil.
Pertimbangkan file appsettings.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:
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;
}
Kode berikut mengonfigurasi opsi bernama:
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();
Kode berikut menampilkan opsi bernama:
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" );
}
}
Semua opsi diberi nama instans. IConfigureOptions<TOptions> instans diperlakukan sebagai menargetkan Options.DefaultName
instans, 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.
OptionsBuilder API
OptionsBuilder<TOptions> digunakan untuk mengonfigurasi TOptions
instans. OptionsBuilder
menyederhanakan pembuatan opsi bernama karena hanya satu parameter ke panggilan awal AddOptions<TOptions>(string optionsName)
alih-alih muncul di semua panggilan berikutnya. Validasi opsi dan ConfigureOptions
kelebihan beban yang menerima dependensi layanan hanya tersedia melalui OptionsBuilder
.
OptionsBuilder
digunakan di bagian Validasi opsi.
Lihat Menggunakan AddOptions untuk mengonfigurasi repositori kustom untuk informasi yang menambahkan repositori kustom.
Menggunakan layanan DI untuk mengonfigurasi opsi
Layanan dapat diakses dari injeksi dependensi sambil mengonfigurasi opsi dengan dua cara:
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<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
Buat jenis yang mengimplementasikan IConfigureOptions<TOptions> atau IConfigureNamedOptions<TOptions> dan mendaftarkan jenis sebagai layanan.
Sebaiknya teruskan delegasi konfigurasi ke Configure, karena membuat layanan lebih kompleks. Membuat jenis setara dengan apa yang dilakukan kerangka kerja saat memanggil Configure. Configure Panggilan mendaftarkan generik IConfigureNamedOptions<TOptions>sementara , yang memiliki konstruktor yang menerima jenis layanan generik yang ditentukan.
Validasi opsi
Validasi opsi memungkinkan nilai opsi divalidasi.
Pertimbangkan file appsettings.json
berikut:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
Kelas berikut digunakan untuk mengikat ke bagian "MyConfig"
konfigurasi dan menerapkan beberapa DataAnnotations
aturan:
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; }
}
Kode berikut:
- AddOptions Panggilan untuk mendapatkan yang OptionsBuilder<TOptions> mengikat ke
MyConfigOptions
kelas . - ValidateDataAnnotations Panggilan untuk mengaktifkan validasi menggunakan
DataAnnotations
.
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations();
var app = builder.Build();
Metode ValidateDataAnnotations
ekstensi ditentukan dalam paket NuGet Microsoft.Extensions.Options.DataAnnotations . Untuk aplikasi web yang menggunakan Microsoft.NET.Sdk.Web
SDK, paket ini dirujuk secara implisit dari kerangka kerja bersama.
Kode berikut menampilkan nilai konfigurasi atau kesalahan validasi:
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);
}
Kode berikut menerapkan aturan validasi yang lebih kompleks menggunakan delegasi:
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>
dan IValidatableObject
Kelas berikut mengimplementasikan 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
memungkinkan pemindahan kode validasi keluar dari Program.cs
dan ke kelas.
Menggunakan kode sebelumnya, validasi diaktifkan dengan Program.cs
kode berikut:
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();
Validasi opsi juga mendukung IValidatableObject. Untuk melakukan validasi tingkat kelas kelas dalam kelas itu sendiri:
- Terapkan
IValidatableObject
antarmuka dan metodenya Validate dalam kelas . - Panggil ValidateDataAnnotations di
Program.cs
.
ValidateOnStart
Validasi opsi berjalan saat pertama kali TOption
instans dibuat. Itu berarti, misalnya, ketika akses IOptionsSnapshot<TOptions>.Value
pertama terjadi dalam alur permintaan atau ketika IOptionsMonitor<TOptions>.Get(string)
dipanggil pada pengaturan yang ada. Setelah pengaturan dimuat ulang, validasi berjalan lagi. Runtime ASP.NET Core menggunakan untuk menyimpan instans OptionsCache<TOptions> opsi setelah dibuat.
Untuk menjalankan validasi opsi dengan bersemangat, saat aplikasi dimulai, panggil ValidateOnStart<TOptions>(OptionsBuilder<TOptions>)di Program.cs
:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
Opsi pasca-konfigurasi
Atur pasca-konfigurasi dengan IPostConfigureOptions<TOptions>. Pasca-konfigurasi berjalan setelah semua IConfigureOptions<TOptions> konfigurasi terjadi:
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 tersedia untuk pasca-konfigurasi opsi bernama:
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();
Gunakan PostConfigureAll untuk mengonfigurasi semua instans konfigurasi pasca-konfigurasi:
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";
});
Opsi akses di Program.cs
Untuk mengakses IOptions<TOptions> atau IOptionsMonitor<TOptions> di Program.cs
, panggil GetRequiredService di WebApplication.Services:
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
Sumber Daya Tambahan:
Oleh Kirk Larkin dan Rick Anderson.
Pola opsi menggunakan kelas untuk menyediakan akses yang sangat diketik ke grup pengaturan terkait. Saat pengaturan konfigurasi diisolasi oleh 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. Untuk informasi selengkapnya, lihat 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.
Mengikat konfigurasi hierarkis
Cara yang lebih disukai untuk membaca nilai konfigurasi terkait adalah menggunakan pola opsi. Misalnya, untuk membaca nilai konfigurasi berikut:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Buat kelas PositionOptions
berikut:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
Kelas opsi:
- Harus nonabstrak dengan konstruktor tanpa parameter publik.
- Semua properti baca-tulis publik dari jenis ini terikat.
- Bidang tidak terikat. Dalam kode sebelumnya,
Position
tidak terikat. BidangPosition
digunakan sehingga string"Position"
tidak perlu dikodekan secara permanen di aplikasi saat mengikat kelas ke penyedia konfigurasi.
Kode berikut:
- Memanggil ConfigurationBinder.Bind untuk mengikat kelas
PositionOptions
ke bagianPosition
. - Menampilkan data konfigurasi
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}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
ConfigurationBinder.Get<T>
mengikat dan mengembalikan jenis yang ditentukan. ConfigurationBinder.Get<T>
mungkin lebih nyaman daripada menggunakan ConfigurationBinder.Bind
. Contoh kode berikut menunjukkan cara menggunakan ConfigurationBinder.Get<T>
dengan kelas PositionOptions
:
public class Test21Model : PageModel
{
private readonly IConfiguration Configuration;
public PositionOptions? positionOptions { get; private set; }
public Test21Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
positionOptions = Configuration.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
Pendekatan alternatif saat menggunakan pola opsi adalah untuk mengikat bagian Position
dan menambahkannya ke kontainer layanan injeksi dependensi. Dalam kode berikut, PositionOptions
ditambahkan ke kontainer layanan dengan Configure dan terikat ke konfigurasi:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
Dengan menggunakan kode sebelumnya, kode berikut membaca opsi posisi:
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}");
}
}
Dalam kode sebelumnya, perubahan pada file konfigurasi JSON 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 Singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Berguna dalam skenario di mana opsi harus dikomputasi ulang pada setiap permintaan. Untuk informasi selengkapnya, lihat Menggunakan IOptionsSnapshot untuk membaca data yang diperbarui.
- Terdaftar sebagai Cakupan dan oleh karena itu tidak dapat disuntikkan ke dalam layanan Singleton.
- Mendukung opsi bernama
- Digunakan untuk mengambil opsi dan mengelola pemberitahuan opsi untuk
TOptions
instans. - Terdaftar sebagai Singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Mendukung:
- Mengubah pemberitahuan
- opsi bernama
- Konfigurasi yang dapat dimuat ulang
- Invalidasi opsi 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 Create metode. Implementasi default mengambil semua yang terdaftar IConfigureOptions<TOptions> dan IPostConfigureOptions<TOptions> dan menjalankan semua konfigurasi terlebih dahulu, diikuti oleh pasca-konfigurasi. Ini membedakan antara IConfigureNamedOptions<TOptions> dan IConfigureOptions<TOptions> dan hanya memanggil antarmuka yang sesuai.
IOptionsMonitorCache<TOptions> digunakan oleh IOptionsMonitor<TOptions> untuk menyimpan TOptions
instans. IOptionsMonitorCache<TOptions> Instans opsi yang tidak valid di monitor sehingga nilai dikomputasi ulang (TryRemove). Nilai dapat diperkenalkan secara manual dengan TryAdd. Metode Clear ini digunakan ketika semua instans bernama harus dibuat ulang sesuai permintaan.
Menggunakan 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 Cakupan dan dikomputasi ulang per permintaan. Untuk informasi selengkapnya, lihat masalah GitHub ini dan Meningkatkan performa pengikatan konfigurasi.
- Perubahan pada konfigurasi dibaca setelah aplikasi dimulai saat menggunakan penyedia konfigurasi yang mendukung pembacaan nilai konfigurasi yang diperbarui.
Perbedaan antara IOptionsMonitor
dan IOptionsSnapshot
adalah bahwa:
IOptionsMonitor
adalah layanan Singleton yang mengambil nilai opsi saat ini kapan saja, yang sangat berguna dalam dependensi singleton.IOptionsSnapshot
adalah layanan Cakupan dan menyediakan rekam jepret opsi pada saatIOptionsSnapshot<T>
objek dibuat. Rekam jepret opsi dirancang untuk digunakan dengan dependensi sementara dan terlingkup.
Kode berikut menggunakan 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}");
}
}
Kode berikut mendaftarkan instans konfigurasi yang MyOptions
mengikat terhadap:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
Dalam kode sebelumnya, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
IOptionsMonitor
Kode berikut mendaftarkan instans konfigurasi yang MyOptions
mengikat.
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
Contoh berikut menggunakan 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}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
Dukungan opsi bernama menggunakan IConfigureNamedOptions
Opsi bernama:
- Berguna saat beberapa bagian konfigurasi mengikat properti yang sama.
- Peka huruf besar/kecil.
Pertimbangkan file appsettings.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:
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;
}
Kode berikut mengonfigurasi opsi bernama:
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();
Kode berikut menampilkan opsi bernama:
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" );
}
}
Semua opsi diberi nama instans. IConfigureOptions<TOptions> instans diperlakukan sebagai menargetkan Options.DefaultName
instans, 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.
OptionsBuilder API
OptionsBuilder<TOptions> digunakan untuk mengonfigurasi TOptions
instans. OptionsBuilder
menyederhanakan pembuatan opsi bernama karena hanya satu parameter ke panggilan awal AddOptions<TOptions>(string optionsName)
alih-alih muncul di semua panggilan berikutnya. Validasi opsi dan ConfigureOptions
kelebihan beban yang menerima dependensi layanan hanya tersedia melalui OptionsBuilder
.
OptionsBuilder
digunakan di bagian Validasi opsi.
Lihat Menggunakan AddOptions untuk mengonfigurasi repositori kustom untuk informasi yang menambahkan repositori kustom.
Menggunakan layanan DI untuk mengonfigurasi opsi
Layanan dapat diakses dari injeksi dependensi sambil mengonfigurasi opsi dengan dua cara:
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<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
Buat jenis yang mengimplementasikan IConfigureOptions<TOptions> atau IConfigureNamedOptions<TOptions> dan mendaftarkan jenis sebagai layanan.
Sebaiknya teruskan delegasi konfigurasi ke Configure, karena membuat layanan lebih kompleks. Membuat jenis setara dengan apa yang dilakukan kerangka kerja saat memanggil Configure. Configure Panggilan mendaftarkan generik IConfigureNamedOptions<TOptions>sementara , yang memiliki konstruktor yang menerima jenis layanan generik yang ditentukan.
Validasi opsi
Validasi opsi memungkinkan nilai opsi divalidasi.
Pertimbangkan file appsettings.json
berikut:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
Kelas berikut digunakan untuk mengikat ke bagian "MyConfig"
konfigurasi dan menerapkan beberapa DataAnnotations
aturan:
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; }
}
Kode berikut:
- AddOptions Panggilan untuk mendapatkan yang OptionsBuilder<TOptions> mengikat ke
MyConfigOptions
kelas . - ValidateDataAnnotations Panggilan untuk mengaktifkan validasi menggunakan
DataAnnotations
.
using OptionsValidationSample.Configuration;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations();
var app = builder.Build();
Metode ValidateDataAnnotations
ekstensi ditentukan dalam paket NuGet Microsoft.Extensions.Options.DataAnnotations . Untuk aplikasi web yang menggunakan Microsoft.NET.Sdk.Web
SDK, paket ini dirujuk secara implisit dari kerangka kerja bersama.
Kode berikut menampilkan nilai konfigurasi atau kesalahan validasi:
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);
}
Kode berikut menerapkan aturan validasi yang lebih kompleks menggunakan delegasi:
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>
dan IValidatableObject
Kelas berikut mengimplementasikan 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
memungkinkan pemindahan kode validasi keluar dari Program.cs
dan ke kelas.
Menggunakan kode sebelumnya, validasi diaktifkan dengan Program.cs
kode berikut:
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();
Validasi opsi juga mendukung IValidatableObject. Untuk melakukan validasi tingkat kelas kelas dalam kelas itu sendiri:
- Terapkan
IValidatableObject
antarmuka dan metodenya Validate dalam kelas . - Panggil ValidateDataAnnotations di
Program.cs
.
ValidateOnStart
Validasi opsi berjalan saat pertama kali IOptions<TOptions>implementasi , , IOptionsSnapshot<TOptions>atau IOptionsMonitor<TOptions> dibuat. Untuk menjalankan validasi opsi dengan bersemangat, saat aplikasi dimulai, panggil ValidateOnStart di Program.cs
:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
Opsi pasca-konfigurasi
Atur pasca-konfigurasi dengan IPostConfigureOptions<TOptions>. Pasca-konfigurasi berjalan setelah semua IConfigureOptions<TOptions> konfigurasi terjadi:
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 tersedia untuk pasca-konfigurasi opsi bernama:
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();
Gunakan PostConfigureAll untuk mengonfigurasi semua instans konfigurasi pasca-konfigurasi:
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";
});
Opsi akses di Program.cs
Untuk mengakses IOptions<TOptions> atau IOptionsMonitor<TOptions> di Program.cs
, panggil GetRequiredService di WebApplication.Services:
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
Sumber Daya Tambahan:
Oleh Kirk Larkin dan Rick Anderson.
Pola opsi menggunakan kelas untuk menyediakan akses yang sangat diketik ke grup pengaturan terkait. Saat pengaturan konfigurasi diisolasi oleh 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. Untuk informasi selengkapnya, lihat bagian Validasi opsi.
Topik ini menyediakan informasi tentang pola opsi di ASP.NET Core. Untuk informasi tentang menggunakan pola opsi di aplikasi konsol, lihat Pola opsi di .NET.
Melihat atau mengunduh kode sampel (cara mengunduh)
Mengikat konfigurasi hierarkis
Cara yang lebih disukai untuk membaca nilai konfigurasi terkait adalah menggunakan pola opsi. Misalnya, untuk membaca nilai konfigurasi berikut:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Buat kelas PositionOptions
berikut:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; }
public string Name { get; set; }
}
Kelas opsi:
- Harus nonabstrak dengan konstruktor tanpa parameter publik.
- Semua properti baca-tulis publik dari jenis ini terikat.
- Bidang tidak terikat. Dalam kode sebelumnya,
Position
tidak terikat. BidangPosition
digunakan sehingga string"Position"
tidak perlu dikodekan secara permanen di aplikasi saat mengikat kelas ke penyedia konfigurasi.
Kode berikut:
- Memanggil ConfigurationBinder.Bind untuk mengikat kelas
PositionOptions
ke bagianPosition
. - Menampilkan data konfigurasi
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}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
ConfigurationBinder.Get<T>
mengikat dan mengembalikan jenis yang ditentukan. ConfigurationBinder.Get<T>
mungkin lebih nyaman daripada menggunakan ConfigurationBinder.Bind
. Contoh kode berikut menunjukkan cara menggunakan ConfigurationBinder.Get<T>
dengan kelas PositionOptions
:
public class Test21Model : PageModel
{
private readonly IConfiguration Configuration;
public PositionOptions positionOptions { get; private set; }
public Test21Model(IConfiguration configuration)
{
Configuration = configuration;
}
public ContentResult OnGet()
{
positionOptions = Configuration.GetSection(PositionOptions.Position)
.Get<PositionOptions>();
return Content($"Title: {positionOptions.Title} \n" +
$"Name: {positionOptions.Name}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
Pendekatan alternatif saat menggunakan pola opsi adalah untuk mengikat bagian Position
dan menambahkannya ke kontainer layanan injeksi dependensi. Dalam kode berikut, PositionOptions
ditambahkan ke kontainer layanan dengan Configure dan terikat ke konfigurasi:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
Dengan menggunakan kode sebelumnya, kode berikut membaca opsi posisi:
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}");
}
}
Dalam kode sebelumnya, perubahan pada file konfigurasi JSON 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 Singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Berguna dalam skenario di mana opsi harus dikomputasi ulang pada setiap permintaan. Untuk informasi selengkapnya, lihat Menggunakan IOptionsSnapshot untuk membaca data yang diperbarui.
- Terdaftar sebagai Cakupan dan oleh karena itu tidak dapat disuntikkan ke dalam layanan Singleton.
- Mendukung opsi bernama
- Digunakan untuk mengambil opsi dan mengelola pemberitahuan opsi untuk
TOptions
instans. - Terdaftar sebagai Singleton dan dapat disuntikkan ke dalam masa pakai layanan apa pun.
- Mendukung:
- Mengubah pemberitahuan
- Opsi bernama
- Konfigurasi yang dapat dimuat ulang
- Invalidasi opsi 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 Create metode. Implementasi default mengambil semua yang terdaftar IConfigureOptions<TOptions> dan IPostConfigureOptions<TOptions> dan menjalankan semua konfigurasi terlebih dahulu, diikuti oleh pasca-konfigurasi. Ini membedakan antara IConfigureNamedOptions<TOptions> dan IConfigureOptions<TOptions> dan hanya memanggil antarmuka yang sesuai.
IOptionsMonitorCache<TOptions> digunakan oleh IOptionsMonitor<TOptions> untuk menyimpan TOptions
instans. IOptionsMonitorCache<TOptions> Instans opsi yang tidak valid di monitor sehingga nilai dikomputasi ulang (TryRemove). Nilai dapat diperkenalkan secara manual dengan TryAdd. Metode Clear ini digunakan ketika semua instans bernama harus dibuat ulang sesuai permintaan.
Menggunakan IOptionsSnapshot untuk membaca data yang diperbarui
Menggunakan IOptionsSnapshot<TOptions>opsi , dihitung sekali per permintaan saat diakses dan di-cache selama masa pakai permintaan. Perubahan pada konfigurasi dibaca setelah aplikasi dimulai saat menggunakan penyedia konfigurasi yang mendukung pembacaan nilai konfigurasi yang diperbarui.
Perbedaan antara IOptionsMonitor
dan IOptionsSnapshot
adalah bahwa:
IOptionsMonitor
adalah layanan Singleton yang mengambil nilai opsi saat ini kapan saja, yang sangat berguna dalam dependensi singleton.IOptionsSnapshot
adalah layanan Cakupan dan menyediakan rekam jepret opsi pada saatIOptionsSnapshot<T>
objek dibuat. Rekam jepret opsi dirancang untuk digunakan dengan dependensi sementara dan terlingkup.
Kode berikut menggunakan 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}");
}
}
Kode berikut mendaftarkan instans konfigurasi yang MyOptions
mengikat terhadap:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
Dalam kode sebelumnya, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
IOptionsMonitor
Kode berikut mendaftarkan instans konfigurasi yang MyOptions
mengikat.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
Contoh berikut menggunakan 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}");
}
}
Dalam kode sebelumnya, secara default, perubahan pada file konfigurasi JSON setelah aplikasi dimulai dibaca.
Dukungan opsi bernama menggunakan IConfigureNamedOptions
Opsi bernama:
- Berguna saat beberapa bagian konfigurasi mengikat properti yang sama.
- Peka huruf besar/kecil.
Pertimbangkan file appsettings.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:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string Name { get; set; }
public string Model { get; set; }
}
Kode berikut mengonfigurasi opsi bernama:
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();
}
Kode berikut menampilkan opsi bernama:
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" );
}
}
Semua opsi diberi nama instans. IConfigureOptions<TOptions> instans diperlakukan sebagai menargetkan Options.DefaultName
instans, 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.
OptionsBuilder API
OptionsBuilder<TOptions> digunakan untuk mengonfigurasi TOptions
instans. OptionsBuilder
menyederhanakan pembuatan opsi bernama karena hanya satu parameter ke panggilan awal AddOptions<TOptions>(string optionsName)
alih-alih muncul di semua panggilan berikutnya. Validasi opsi dan ConfigureOptions
kelebihan beban yang menerima dependensi layanan hanya tersedia melalui OptionsBuilder
.
OptionsBuilder
digunakan di bagian Validasi opsi.
Lihat Menggunakan AddOptions untuk mengonfigurasi repositori kustom untuk informasi yang menambahkan repositori kustom.
Menggunakan layanan DI untuk mengonfigurasi opsi
Layanan dapat diakses dari injeksi dependensi sambil mengonfigurasi opsi dengan dua cara:
Teruskan delegasi konfigurasi ke Configure pada OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>
menyediakan kelebihan beban Configure yang memungkinkan penggunaan hingga lima layanan untuk mengonfigurasi opsi:services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
Buat jenis yang mengimplementasikan IConfigureOptions<TOptions> atau IConfigureNamedOptions<TOptions> dan mendaftarkan jenis sebagai layanan.
Sebaiknya teruskan delegasi konfigurasi ke Configure, karena membuat layanan lebih kompleks. Membuat jenis setara dengan apa yang dilakukan kerangka kerja saat memanggil Configure. Configure Panggilan mendaftarkan generik IConfigureNamedOptions<TOptions>sementara , yang memiliki konstruktor yang menerima jenis layanan generik yang ditentukan.
Validasi opsi
Validasi opsi memungkinkan nilai opsi divalidasi.
Pertimbangkan file appsettings.json
berikut:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
Kelas berikut mengikat ke bagian "MyConfig"
konfigurasi dan menerapkan beberapa DataAnnotations
aturan:
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; }
}
Kode berikut:
- AddOptions Panggilan untuk mendapatkan yang OptionsBuilder<TOptions> mengikat ke
MyConfigOptions
kelas . - ValidateDataAnnotations Panggilan untuk mengaktifkan validasi menggunakan
DataAnnotations
.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions<MyConfigOptions>()
.Bind(Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations();
services.AddControllersWithViews();
}
Metode ValidateDataAnnotations
ekstensi ditentukan dalam paket NuGet Microsoft.Extensions.Options.DataAnnotations . Untuk aplikasi web yang menggunakan Microsoft.NET.Sdk.Web
SDK, paket ini dirujuk secara implisit dari kerangka kerja bersama.
Kode berikut menampilkan nilai konfigurasi atau kesalahan validasi:
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);
}
Kode berikut menerapkan aturan validasi yang lebih kompleks menggunakan delegasi:
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 untuk validasi kompleks
Kelas berikut mengimplementasikan 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
memungkinkan pemindahan kode validasi keluar dari StartUp
dan ke kelas.
Menggunakan kode sebelumnya, validasi diaktifkan dengan Startup.ConfigureServices
kode berikut:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfigOptions>(Configuration.GetSection(
MyConfigOptions.MyConfig));
services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
<MyConfigOptions>, MyConfigValidation>());
services.AddControllersWithViews();
}
Opsi pasca-konfigurasi
Atur pasca-konfigurasi dengan IPostConfigureOptions<TOptions>. Pasca-konfigurasi berjalan setelah semua IConfigureOptions<TOptions> konfigurasi terjadi:
services.PostConfigure<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
PostConfigure tersedia untuk pasca-konfigurasi opsi bernama:
services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Gunakan PostConfigureAll untuk mengonfigurasi semua instans konfigurasi pasca-konfigurasi:
services.PostConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Mengakses opsi selama startup
IOptions<TOptions> dan IOptionsMonitor<TOptions> dapat digunakan dalam Startup.Configure
, karena layanan dibangun sebelum Configure
metode dijalankan.
public void Configure(IApplicationBuilder app,
IOptionsMonitor<MyOptions> optionsAccessor)
{
var option1 = optionsAccessor.CurrentValue.Option1;
}
Jangan gunakan IOptions<TOptions> atau IOptionsMonitor<TOptions> di Startup.ConfigureServices
. Status opsi yang tidak konsisten mungkin ada karena pemesanan pendaftaran layanan.
Paket NuGet Options.ConfigurationExtensions
Paket Microsoft.Extensions.Options.ConfigurationExtensions dirujuk secara implisit di aplikasi ASP.NET Core.
ASP.NET Core