Wzorzec opcji na platformie ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Autor: Rick Anderson.
Wzorzec opcji używa klas w celu zapewnienia silnie typizowanego dostępu do grup powiązanych ustawień. Gdy ustawienia konfiguracji są izolowane według scenariusza w oddzielnych klasach, aplikacja jest zgodna z dwoma ważnymi zasadami inżynierii oprogramowania:
- Hermetyzacja:
- Klasy zależne od ustawień konfiguracji zależą tylko od używanych ustawień konfiguracji.
- Separacja zagadnień:
- Ustawienia dla różnych części aplikacji nie są zależne ani powiązane ze sobą.
Opcje udostępniają również mechanizm sprawdzania poprawności danych konfiguracji. Aby uzyskać więcej informacji, zobacz sekcję Walidacja opcji.
Ten artykuł zawiera informacje na temat wzorca opcji w programie ASP.NET Core. Aby uzyskać informacje na temat używania wzorca opcji w aplikacjach konsoli, zobacz Wzorzec opcji na platformie .NET.
Konfiguracja hierarchiczna powiązania
Preferowanym sposobem odczytywania powiązanych wartości konfiguracji jest użycie wzorca opcji. Aby na przykład odczytać następujące wartości konfiguracji:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Utwórz następującą klasę PositionOptions
:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
Klasa opcji:
- Musi być nieskonstrakcji.
- Ma publiczne właściwości odczytu i zapisu typu, które mają odpowiednie elementy w konfiguracji są powiązane.
- Ma właściwości odczytu i zapisu powiązane z pasującymi wpisami w konfiguracji.
- Nie ma powiązanych pól. W poprzednim kodzie element
Position
nie jest powiązany. Dzięki użyciu polaPosition
ciąg"Position"
nie musi być ustalony w aplikacji w przypadku tworzenia powiązania klasy z dostawcą konfiguracji.
Następujący kod powoduje:
- Wywołanie elementu ConfigurationBinder.Bind w celu powiązania klasy
PositionOptions
z sekcjąPosition
. - Wyświetlenie danych konfiguracji
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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Element ConfigurationBinder.Get<T>
tworzy powiązanie i zwraca określony typ. Element ConfigurationBinder.Get<T>
może być wygodniejszy w użyciu niż element ConfigurationBinder.Bind
. W poniższym kodzie pokazano sposób użycia elementu ConfigurationBinder.Get<T>
z klasą 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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Powiązanie umożliwia również konkrecję klasy abstrakcyjnej. Rozważ następujący kod, który używa klasy SomethingWithAName
abstrakcyjnej :
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;
}
Poniższy kod wyświetla NameTitleOptions
wartości konfiguracji:
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}"
);
}
}
Wywołania do elementu Bind
są mniej rygorystyczne niż wywołania do elementu Get<>
:
Bind
umożliwia konkrecję abstrakcyjnego.Get<>
musi utworzyć samo wystąpienie.
Wzorzec opcji
Alternatywne podejście w przypadku korzystania z wzorca opcji to utworzenie powiązania sekcji Position
i dodanie go do kontenera usługi wstrzykiwania zależności. W poniższym kodzie element PositionOptions
jest dodawany do kontenera usługi przy użyciu Configure, a następnie jest tworzone jego powiązanie z konfiguracją:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
Przy użyciu poprzedniego kodu następujący kod odczytuje opcje położenia:
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}");
}
}
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji nie są odczytywane. Aby odczytać zmiany po uruchomieniu aplikacji, użyj elementu IOptionsSnapshot.
Interfejsy opcji
- Nie obsługuje:
- Odczytywanie danych konfiguracji po uruchomieniu aplikacji.
- Opcje nazwane
- Jest rejestrowany jako pojedynczy i może być wstrzykiwany do dowolnego okresu istnienia usługi.
- Jest przydatna w scenariuszach, w których opcje powinny być ponownie obliczane na każdym żądaniu. Aby uzyskać więcej informacji, zobacz Używanie funkcji IOptionsSnapshot do odczytywania zaktualizowanych danych.
- Jest zarejestrowany jako Zakres i dlatego nie można go wstrzykiwać do usługi Singleton.
- Obsługuje opcje nazwane
- Służy do pobierania opcji i zarządzania powiadomieniami o opcjach dla
TOptions
wystąpień. - Jest rejestrowany jako pojedynczy i może być wstrzykiwany do dowolnego okresu istnienia usługi.
- Obsługuje:
- Zmienianie powiadomień
- nazwane opcje
- Konfiguracja z możliwością ponownego ładowania
- Unieważnienie opcji selektywnych (IOptionsMonitorCache<TOptions>)
Scenariusze po konfiguracji umożliwiają ustawienie lub zmianę opcji po zakończeniu wszystkich IConfigureOptions<TOptions> konfiguracji.
IOptionsFactory<TOptions> jest odpowiedzialny za tworzenie nowych wystąpień opcji. Ma jedną Create metodę. Domyślna implementacja pobiera wszystkie zarejestrowane IConfigureOptions<TOptions> i IPostConfigureOptions<TOptions> uruchamia najpierw wszystkie konfiguracje, a następnie po konfiguracji. Rozróżnia między elementami IConfigureNamedOptions<TOptions> i IConfigureOptions<TOptions> i wywołuje tylko odpowiedni interfejs.
IOptionsMonitorCache<TOptions> jest używany przez IOptionsMonitor<TOptions> program do buforowania TOptions
wystąpień. Unieważnia IOptionsMonitorCache<TOptions> wystąpienia opcji w monitorze, aby wartość została ponownie skompilowana (TryRemove). Wartości można wprowadzić ręcznie za pomocą TryAddpolecenia . Metoda Clear jest używana, gdy wszystkie nazwane wystąpienia powinny być ponownie tworzone na żądanie.
Odczytywanie zaktualizowanych danych przy użyciu funkcji IOptionsSnapshot
Za pomocą polecenia IOptionsSnapshot<TOptions>:
- Opcje są obliczane raz na żądanie w przypadku uzyskania dostępu do żądania i buforowania przez okres istnienia żądania.
- Może spowodować znaczną karę za wydajność, ponieważ jest to usługa o określonym zakresie i jest ponownie obliczana na żądanie. Aby uzyskać więcej informacji, zobacz ten problem z usługą GitHub i Zwiększ wydajność powiązania konfiguracji.
- Zmiany konfiguracji są odczytywane po uruchomieniu aplikacji podczas korzystania z dostawców konfiguracji obsługujących odczytywanie zaktualizowanych wartości konfiguracji.
Różnica między elementami IOptionsMonitor
i IOptionsSnapshot
polega na tym, że:
IOptionsMonitor
jest usługą Singleton, która w dowolnym momencie pobiera bieżące wartości opcji, co jest szczególnie przydatne w zależnościach pojedynczych.IOptionsSnapshot
jest usługą o określonym zakresie i udostępnia migawkę opcji w momencieIOptionsSnapshot<T>
konstruowania obiektu. Migawki opcji są przeznaczone do użytku z zależnościami przejściowymi i o określonym zakresie.
Poniższy kod używa metody 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}");
}
}
Poniższy kod rejestruje wystąpienie konfiguracji, które MyOptions
wiąże się z:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
IOptionsMonitor
Poniższy kod rejestruje wystąpienie konfiguracji, które MyOptions
jest powiązane z.
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
W poniższym przykładzie użyto metody 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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Obsługa opcji nazwanych przy użyciu funkcji IConfigureNamedOptions
Nazwane opcje:
- Są przydatne, gdy wiele sekcji konfiguracji wiąże się z tymi samymi właściwościami.
- Uwzględnia wielkość liter.
Rozważ użycie następującego pliku appsettings.json
:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
Zamiast tworzyć dwie klasy do powiązania TopItem:Month
i TopItem:Year
, dla każdej sekcji jest używana następująca klasa:
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;
}
Poniższy kod konfiguruje nazwane opcje:
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();
Poniższy kod wyświetla nazwane opcje:
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" );
}
}
Wszystkie opcje są nazwane wystąpienia. IConfigureOptions<TOptions> wystąpienia są traktowane jako docelowe Options.DefaultName
wystąpienie, czyli string.Empty
. IConfigureNamedOptions<TOptions> implementuje IConfigureOptions<TOptions>również funkcję . Domyślna implementacja elementu IOptionsFactory<TOptions> ma logikę do użycia odpowiednio. Nazwana null
opcja służy do określania wartości docelowej wszystkich nazwanych wystąpień zamiast określonego nazwanego wystąpienia. ConfigureAll i PostConfigureAll stosować tę konwencję.
OptionsBuilder API
OptionsBuilder<TOptions> służy do konfigurowania TOptions
wystąpień. OptionsBuilder
Usprawnia tworzenie nazwanych opcji, ponieważ jest to tylko jeden parametr do początkowego AddOptions<TOptions>(string optionsName)
wywołania zamiast pojawiać się we wszystkich kolejnych wywołaniach. Opcje weryfikacji i ConfigureOptions
przeciążenia akceptujące zależności usługi są dostępne tylko za pośrednictwem .OptionsBuilder
OptionsBuilder
jest używany w sekcji Walidacja opcji.
Aby uzyskać informacje na temat dodawania repozytorium niestandardowego, zobacz Use AddOptions (Używanie polecenia AddOptions do konfigurowania repozytorium niestandardowego).
Konfigurowanie opcji przy użyciu usług DI
Dostęp do usług można uzyskać z iniekcji zależności podczas konfigurowania opcji na dwa sposoby:
Przekaż delegata konfiguracji do Configure adresu w dniu OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>
Zapewnia przeciążenia Configure , które umożliwiają korzystanie z maksymalnie pięciu usług do konfigurowania opcji: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));
Utwórz typ implementujący IConfigureOptions<TOptions> lub IConfigureNamedOptions<TOptions> rejestrujący typ jako usługę.
Zalecamy przekazanie delegata konfiguracji do Configureelementu , ponieważ tworzenie usługi jest bardziej złożone. Tworzenie typu jest równoważne z tym, co robi platforma podczas wywoływania metody Configure. Wywoływanie Configure powoduje zarejestrowanie przejściowego ogólnego IConfigureNamedOptions<TOptions>, który ma konstruktor, który akceptuje określone typy usług ogólnych.
Walidacja opcji
Walidacja opcji umożliwia zweryfikowanie wartości opcji.
Rozważ użycie następującego pliku appsettings.json
:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
Następująca klasa służy do powiązania z sekcją "MyConfig"
konfiguracji i stosuje kilka DataAnnotations
reguł:
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; }
}
Następujący kod powoduje:
- Wywołania AddOptions w celu uzyskania elementu OptionsBuilder<TOptions> powiązanego z klasą
MyConfigOptions
. - Wywołania ValidateDataAnnotations w celu włączenia walidacji przy użyciu polecenia
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();
Metoda rozszerzenia jest definiowana ValidateDataAnnotations
w pakiecie NuGet Microsoft.Extensions.Options.DataAnnotations . W przypadku aplikacji internetowych korzystających z Microsoft.NET.Sdk.Web
zestawu SDK ten pakiet jest odwołuje się niejawnie z udostępnionej platformy.
Poniższy kod wyświetla wartości konfiguracji lub błędy walidacji:
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);
}
Poniższy kod stosuje bardziej złożoną regułę walidacji przy użyciu delegata:
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>
i IValidatableObject
Następująca klasa implementuje IValidateOptions<TOptions>element :
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
umożliwia przeniesienie kodu weryfikacji z Program.cs
klasy i do klasy.
Korzystając z powyższego kodu, walidacja jest włączona przy Program.cs
użyciu następującego kodu:
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();
Walidacja opcji obsługuje również funkcję IValidatableObject. Aby przeprowadzić walidację klasy na poziomie klasy w obrębie samej klasy:
- Zaimplementuj
IValidatableObject
interfejs i jego Validate metodę w klasie . - Wywołaj metodę ValidateDataAnnotations w pliku
Program.cs
.
ValidateOnStart
Walidacja opcji jest uruchamiana przy pierwszym utworzeniu TOption
wystąpienia. Oznacza to na przykład, że gdy pierwszy dostęp ma IOptionsSnapshot<TOptions>.Value
miejsce w potoku żądania lub gdy IOptionsMonitor<TOptions>.Get(string)
jest wywoływany w ustawieniach obecnych. Po ponownym załadowaniu ustawień walidacja zostanie uruchomiona ponownie. Środowisko uruchomieniowe ASP.NET Core używa OptionsCache<TOptions> do buforowania wystąpienia opcji po jego utworzeniu.
Aby uruchomić walidację opcji z niecierpliwością, po uruchomieniu aplikacji wywołaj metodę w pliku ValidateOnStart<TOptions>(OptionsBuilder<TOptions>)Program.cs
:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
Opcje po konfiguracji
Ustaw konfigurację po konfiguracji za pomocą polecenia IPostConfigureOptions<TOptions>. Po zakończeniu konfiguracji wszystkie IConfigureOptions<TOptions> konfiguracje są uruchamiane:
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 program jest dostępny do po skonfigurowaniu nazwanych opcji:
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();
Użyj PostConfigureAll polecenia , aby po skonfigurowaniu wszystkich wystąpień konfiguracji:
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";
});
Opcje dostępu w programie Program.cs
Aby uzyskać dostęp do IOptions<TOptions> polecenia lub IOptionsMonitor<TOptions> w Program.cs
usłudze , wywołaj metodę GetRequiredService :WebApplication.Services
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
Dodatkowe zasoby
Przez Kirk Larkin i Rick Anderson.
Wzorzec opcji używa klas w celu zapewnienia silnie typizowanego dostępu do grup powiązanych ustawień. Gdy ustawienia konfiguracji są izolowane według scenariusza w oddzielnych klasach, aplikacja jest zgodna z dwoma ważnymi zasadami inżynierii oprogramowania:
- Hermetyzacja:
- Klasy zależne od ustawień konfiguracji zależą tylko od używanych ustawień konfiguracji.
- Separacja zagadnień:
- Ustawienia dla różnych części aplikacji nie są zależne ani powiązane ze sobą.
Opcje udostępniają również mechanizm sprawdzania poprawności danych konfiguracji. Aby uzyskać więcej informacji, zobacz sekcję Walidacja opcji.
Ten artykuł zawiera informacje na temat wzorca opcji w programie ASP.NET Core. Aby uzyskać informacje na temat używania wzorca opcji w aplikacjach konsoli, zobacz Wzorzec opcji na platformie .NET.
Konfiguracja hierarchiczna powiązania
Preferowanym sposobem odczytywania powiązanych wartości konfiguracji jest użycie wzorca opcji. Aby na przykład odczytać następujące wartości konfiguracji:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Utwórz następującą klasę PositionOptions
:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; } = String.Empty;
public string Name { get; set; } = String.Empty;
}
Klasa opcji:
- Musi być nieabstrakcyjna z publicznym konstruktorem bez parametrów.
- Wszystkie publiczne właściwości typu do odczytu i zapisu są powiązane.
- Pola nie są powiązane. W poprzednim kodzie element
Position
nie jest powiązany. Dzięki użyciu polaPosition
ciąg"Position"
nie musi być ustalony w aplikacji w przypadku tworzenia powiązania klasy z dostawcą konfiguracji.
Następujący kod powoduje:
- Wywołanie elementu ConfigurationBinder.Bind w celu powiązania klasy
PositionOptions
z sekcjąPosition
. - Wyświetlenie danych konfiguracji
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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Element ConfigurationBinder.Get<T>
tworzy powiązanie i zwraca określony typ. Element ConfigurationBinder.Get<T>
może być wygodniejszy w użyciu niż element ConfigurationBinder.Bind
. W poniższym kodzie pokazano sposób użycia elementu ConfigurationBinder.Get<T>
z klasą 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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Alternatywne podejście w przypadku korzystania z wzorca opcji to utworzenie powiązania sekcji Position
i dodanie go do kontenera usługi wstrzykiwania zależności. W poniższym kodzie element PositionOptions
jest dodawany do kontenera usługi przy użyciu Configure, a następnie jest tworzone jego powiązanie z konfiguracją:
using ConfigSample.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<PositionOptions>(
builder.Configuration.GetSection(PositionOptions.Position));
var app = builder.Build();
Przy użyciu poprzedniego kodu następujący kod odczytuje opcje położenia:
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}");
}
}
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji nie są odczytywane. Aby odczytać zmiany po uruchomieniu aplikacji, użyj elementu IOptionsSnapshot.
Interfejsy opcji
- Nie obsługuje:
- Odczytywanie danych konfiguracji po uruchomieniu aplikacji.
- Opcje nazwane
- Jest rejestrowany jako pojedynczy i może być wstrzykiwany do dowolnego okresu istnienia usługi.
- Jest przydatna w scenariuszach, w których opcje powinny być ponownie obliczane na każdym żądaniu. Aby uzyskać więcej informacji, zobacz Używanie funkcji IOptionsSnapshot do odczytywania zaktualizowanych danych.
- Jest zarejestrowany jako Zakres i dlatego nie można go wstrzykiwać do usługi Singleton.
- Obsługuje opcje nazwane
- Służy do pobierania opcji i zarządzania powiadomieniami o opcjach dla
TOptions
wystąpień. - Jest rejestrowany jako pojedynczy i może być wstrzykiwany do dowolnego okresu istnienia usługi.
- Obsługuje:
- Zmienianie powiadomień
- nazwane opcje
- Konfiguracja z możliwością ponownego ładowania
- Unieważnienie opcji selektywnych (IOptionsMonitorCache<TOptions>)
Scenariusze po konfiguracji umożliwiają ustawienie lub zmianę opcji po zakończeniu wszystkich IConfigureOptions<TOptions> konfiguracji.
IOptionsFactory<TOptions> jest odpowiedzialny za tworzenie nowych wystąpień opcji. Ma jedną Create metodę. Domyślna implementacja pobiera wszystkie zarejestrowane IConfigureOptions<TOptions> i IPostConfigureOptions<TOptions> uruchamia najpierw wszystkie konfiguracje, a następnie po konfiguracji. Rozróżnia między elementami IConfigureNamedOptions<TOptions> i IConfigureOptions<TOptions> i wywołuje tylko odpowiedni interfejs.
IOptionsMonitorCache<TOptions> jest używany przez IOptionsMonitor<TOptions> program do buforowania TOptions
wystąpień. Unieważnia IOptionsMonitorCache<TOptions> wystąpienia opcji w monitorze, aby wartość została ponownie skompilowana (TryRemove). Wartości można wprowadzić ręcznie za pomocą TryAddpolecenia . Metoda Clear jest używana, gdy wszystkie nazwane wystąpienia powinny być ponownie tworzone na żądanie.
Odczytywanie zaktualizowanych danych przy użyciu funkcji IOptionsSnapshot
Za pomocą polecenia IOptionsSnapshot<TOptions>:
- Opcje są obliczane raz na żądanie w przypadku uzyskania dostępu do żądania i buforowania przez okres istnienia żądania.
- Może spowodować znaczną karę za wydajność, ponieważ jest to usługa o określonym zakresie i jest ponownie obliczana na żądanie. Aby uzyskać więcej informacji, zobacz ten problem z usługą GitHub i Zwiększ wydajność powiązania konfiguracji.
- Zmiany konfiguracji są odczytywane po uruchomieniu aplikacji podczas korzystania z dostawców konfiguracji obsługujących odczytywanie zaktualizowanych wartości konfiguracji.
Różnica między elementami IOptionsMonitor
i IOptionsSnapshot
polega na tym, że:
IOptionsMonitor
jest usługą Singleton, która w dowolnym momencie pobiera bieżące wartości opcji, co jest szczególnie przydatne w zależnościach pojedynczych.IOptionsSnapshot
jest usługą o określonym zakresie i udostępnia migawkę opcji w momencieIOptionsSnapshot<T>
konstruowania obiektu. Migawki opcji są przeznaczone do użytku z zależnościami przejściowymi i o określonym zakresie.
Poniższy kod używa metody 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}");
}
}
Poniższy kod rejestruje wystąpienie konfiguracji, które MyOptions
wiąże się z:
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
IOptionsMonitor
Poniższy kod rejestruje wystąpienie konfiguracji, które MyOptions
jest powiązane z.
using SampleApp.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions"));
var app = builder.Build();
W poniższym przykładzie użyto metody 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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Obsługa opcji nazwanych przy użyciu funkcji IConfigureNamedOptions
Nazwane opcje:
- Są przydatne, gdy wiele sekcji konfiguracji wiąże się z tymi samymi właściwościami.
- Uwzględnia wielkość liter.
Rozważ użycie następującego pliku appsettings.json
:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
Zamiast tworzyć dwie klasy do powiązania TopItem:Month
i TopItem:Year
, dla każdej sekcji jest używana następująca klasa:
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;
}
Poniższy kod konfiguruje nazwane opcje:
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();
Poniższy kod wyświetla nazwane opcje:
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" );
}
}
Wszystkie opcje są nazwane wystąpienia. IConfigureOptions<TOptions> wystąpienia są traktowane jako docelowe Options.DefaultName
wystąpienie, czyli string.Empty
. IConfigureNamedOptions<TOptions> implementuje IConfigureOptions<TOptions>również funkcję . Domyślna implementacja elementu IOptionsFactory<TOptions> ma logikę do użycia odpowiednio. Nazwana null
opcja służy do określania wartości docelowej wszystkich nazwanych wystąpień zamiast określonego nazwanego wystąpienia. ConfigureAll i PostConfigureAll stosować tę konwencję.
OptionsBuilder API
OptionsBuilder<TOptions> służy do konfigurowania TOptions
wystąpień. OptionsBuilder
Usprawnia tworzenie nazwanych opcji, ponieważ jest to tylko jeden parametr do początkowego AddOptions<TOptions>(string optionsName)
wywołania zamiast pojawiać się we wszystkich kolejnych wywołaniach. Opcje weryfikacji i ConfigureOptions
przeciążenia akceptujące zależności usługi są dostępne tylko za pośrednictwem .OptionsBuilder
OptionsBuilder
jest używany w sekcji Walidacja opcji.
Aby uzyskać informacje na temat dodawania repozytorium niestandardowego, zobacz Use AddOptions (Używanie polecenia AddOptions do konfigurowania repozytorium niestandardowego).
Konfigurowanie opcji przy użyciu usług DI
Dostęp do usług można uzyskać z iniekcji zależności podczas konfigurowania opcji na dwa sposoby:
Przekaż delegata konfiguracji do Configure adresu w dniu OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>
Zapewnia przeciążenia Configure , które umożliwiają korzystanie z maksymalnie pięciu usług do konfigurowania opcji: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));
Utwórz typ implementujący IConfigureOptions<TOptions> lub IConfigureNamedOptions<TOptions> rejestrujący typ jako usługę.
Zalecamy przekazanie delegata konfiguracji do Configureelementu , ponieważ tworzenie usługi jest bardziej złożone. Tworzenie typu jest równoważne z tym, co robi platforma podczas wywoływania metody Configure. Wywoływanie Configure powoduje zarejestrowanie przejściowego ogólnego IConfigureNamedOptions<TOptions>, który ma konstruktor, który akceptuje określone typy usług ogólnych.
Walidacja opcji
Walidacja opcji umożliwia zweryfikowanie wartości opcji.
Rozważ użycie następującego pliku appsettings.json
:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
Następująca klasa służy do powiązania z sekcją "MyConfig"
konfiguracji i stosuje kilka DataAnnotations
reguł:
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; }
}
Następujący kod powoduje:
- Wywołania AddOptions w celu uzyskania elementu OptionsBuilder<TOptions> powiązanego z klasą
MyConfigOptions
. - Wywołania ValidateDataAnnotations w celu włączenia walidacji przy użyciu polecenia
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();
Metoda rozszerzenia jest definiowana ValidateDataAnnotations
w pakiecie NuGet Microsoft.Extensions.Options.DataAnnotations . W przypadku aplikacji internetowych korzystających z Microsoft.NET.Sdk.Web
zestawu SDK ten pakiet jest odwołuje się niejawnie z udostępnionej platformy.
Poniższy kod wyświetla wartości konfiguracji lub błędy walidacji:
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);
}
Poniższy kod stosuje bardziej złożoną regułę walidacji przy użyciu delegata:
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>
i IValidatableObject
Następująca klasa implementuje IValidateOptions<TOptions>element :
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
umożliwia przeniesienie kodu weryfikacji z Program.cs
klasy i do klasy.
Korzystając z powyższego kodu, walidacja jest włączona przy Program.cs
użyciu następującego kodu:
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();
Walidacja opcji obsługuje również funkcję IValidatableObject. Aby przeprowadzić walidację klasy na poziomie klasy w obrębie samej klasy:
- Zaimplementuj
IValidatableObject
interfejs i jego Validate metodę w klasie . - Wywołaj metodę ValidateDataAnnotations w pliku
Program.cs
.
ValidateOnStart
Walidacja opcji jest uruchamiana po raz pierwszy IOptions<TOptions>podczas tworzenia implementacji , IOptionsSnapshot<TOptions>lub IOptionsMonitor<TOptions> . Aby uruchomić walidację opcji z niecierpliwością, po uruchomieniu aplikacji wywołaj metodę w pliku ValidateOnStart Program.cs
:
builder.Services.AddOptions<MyConfigOptions>()
.Bind(builder.Configuration.GetSection(MyConfigOptions.MyConfig))
.ValidateDataAnnotations()
.ValidateOnStart();
Opcje po konfiguracji
Ustaw konfigurację po konfiguracji za pomocą polecenia IPostConfigureOptions<TOptions>. Po zakończeniu konfiguracji wszystkie IConfigureOptions<TOptions> konfiguracje są uruchamiane:
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 program jest dostępny do po skonfigurowaniu nazwanych opcji:
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();
Użyj PostConfigureAll polecenia , aby po skonfigurowaniu wszystkich wystąpień konfiguracji:
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";
});
Opcje dostępu w programie Program.cs
Aby uzyskać dostęp do IOptions<TOptions> polecenia lub IOptionsMonitor<TOptions> w Program.cs
usłudze , wywołaj metodę GetRequiredService :WebApplication.Services
var app = builder.Build();
var option1 = app.Services.GetRequiredService<IOptionsMonitor<MyOptions>>()
.CurrentValue.Option1;
Dodatkowe zasoby
Przez Kirk Larkin i Rick Anderson.
Wzorzec opcji używa klas w celu zapewnienia silnie typizowanego dostępu do grup powiązanych ustawień. Gdy ustawienia konfiguracji są izolowane według scenariusza w oddzielnych klasach, aplikacja jest zgodna z dwoma ważnymi zasadami inżynierii oprogramowania:
- Hermetyzacja:
- Klasy zależne od ustawień konfiguracji zależą tylko od używanych ustawień konfiguracji.
- Separacja zagadnień:
- Ustawienia dla różnych części aplikacji nie są zależne ani powiązane ze sobą.
Opcje udostępniają również mechanizm sprawdzania poprawności danych konfiguracji. Aby uzyskać więcej informacji, zobacz sekcję Walidacja opcji.
Ten temat zawiera informacje na temat wzorca opcji w programie ASP.NET Core. Aby uzyskać informacje na temat używania wzorca opcji w aplikacjach konsoli, zobacz Wzorzec opcji na platformie .NET.
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Konfiguracja hierarchiczna powiązania
Preferowanym sposobem odczytywania powiązanych wartości konfiguracji jest użycie wzorca opcji. Aby na przykład odczytać następujące wartości konfiguracji:
"Position": {
"Title": "Editor",
"Name": "Joe Smith"
}
Utwórz następującą klasę PositionOptions
:
public class PositionOptions
{
public const string Position = "Position";
public string Title { get; set; }
public string Name { get; set; }
}
Klasa opcji:
- Musi być nieabstrakcyjna z publicznym konstruktorem bez parametrów.
- Wszystkie publiczne właściwości typu do odczytu i zapisu są powiązane.
- Pola nie są powiązane. W poprzednim kodzie element
Position
nie jest powiązany. Dzięki użyciu właściwościPosition
ciąg"Position"
nie musi być ustalony w aplikacji w przypadku tworzenia powiązania klasy z dostawcą konfiguracji.
Następujący kod powoduje:
- Wywołanie elementu ConfigurationBinder.Bind w celu powiązania klasy
PositionOptions
z sekcjąPosition
. - Wyświetlenie danych konfiguracji
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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Element ConfigurationBinder.Get<T>
tworzy powiązanie i zwraca określony typ. Element ConfigurationBinder.Get<T>
może być wygodniejszy w użyciu niż element ConfigurationBinder.Bind
. W poniższym kodzie pokazano sposób użycia elementu ConfigurationBinder.Get<T>
z klasą 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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Alternatywne podejście w przypadku korzystania z wzorca opcji to utworzenie powiązania sekcji Position
i dodanie go do kontenera usługi wstrzykiwania zależności. W poniższym kodzie element PositionOptions
jest dodawany do kontenera usługi przy użyciu Configure, a następnie jest tworzone jego powiązanie z konfiguracją:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
Przy użyciu poprzedniego kodu następujący kod odczytuje opcje położenia:
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}");
}
}
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji nie są odczytywane. Aby odczytać zmiany po uruchomieniu aplikacji, użyj elementu IOptionsSnapshot.
Interfejsy opcji
- Nie obsługuje:
- Odczytywanie danych konfiguracji po uruchomieniu aplikacji.
- Opcje nazwane
- Jest rejestrowany jako pojedynczy i może być wstrzykiwany do dowolnego okresu istnienia usługi.
- Jest przydatna w scenariuszach, w których opcje powinny być ponownie obliczane na każdym żądaniu. Aby uzyskać więcej informacji, zobacz Używanie funkcji IOptionsSnapshot do odczytywania zaktualizowanych danych.
- Jest zarejestrowany jako Zakres i dlatego nie można go wstrzyknąć do usługi Singleton.
- Obsługuje opcje nazwane
- Służy do pobierania opcji i zarządzania powiadomieniami o opcjach dla
TOptions
wystąpień. - Jest rejestrowany jako pojedynczy i może być wstrzykiwany do dowolnego okresu istnienia usługi.
- Obsługuje:
- Zmienianie powiadomień
- Opcje nazwane
- Konfiguracja z możliwością ponownego ładowania
- Unieważnienie opcji selektywnych (IOptionsMonitorCache<TOptions>)
Scenariusze po konfiguracji umożliwiają ustawienie lub zmianę opcji po zakończeniu wszystkich IConfigureOptions<TOptions> konfiguracji.
IOptionsFactory<TOptions> jest odpowiedzialny za tworzenie nowych wystąpień opcji. Ma jedną Create metodę. Domyślna implementacja pobiera wszystkie zarejestrowane IConfigureOptions<TOptions> i IPostConfigureOptions<TOptions> uruchamia najpierw wszystkie konfiguracje, a następnie po konfiguracji. Rozróżnia między elementami IConfigureNamedOptions<TOptions> i IConfigureOptions<TOptions> i wywołuje tylko odpowiedni interfejs.
IOptionsMonitorCache<TOptions> jest używany przez IOptionsMonitor<TOptions> program do buforowania TOptions
wystąpień. Unieważnia IOptionsMonitorCache<TOptions> wystąpienia opcji w monitorze, aby wartość została ponownie skompilowana (TryRemove). Wartości można wprowadzić ręcznie za pomocą TryAddpolecenia . Metoda Clear jest używana, gdy wszystkie nazwane wystąpienia powinny być ponownie tworzone na żądanie.
Odczytywanie zaktualizowanych danych przy użyciu funkcji IOptionsSnapshot
Przy użyciu IOptionsSnapshot<TOptions>opcji opcje są obliczane raz na żądanie w przypadku uzyskania dostępu do żądania i buforowania przez okres istnienia żądania. Zmiany konfiguracji są odczytywane po uruchomieniu aplikacji podczas korzystania z dostawców konfiguracji obsługujących odczytywanie zaktualizowanych wartości konfiguracji.
Różnica między elementami IOptionsMonitor
i IOptionsSnapshot
polega na tym, że:
IOptionsMonitor
jest usługą Singleton, która w dowolnym momencie pobiera bieżące wartości opcji, co jest szczególnie przydatne w zależnościach pojedynczych.IOptionsSnapshot
jest usługą o określonym zakresie i udostępnia migawkę opcji w momencieIOptionsSnapshot<T>
konstruowania obiektu. Migawki opcji są przeznaczone do użytku z zależnościami przejściowymi i o określonym zakresie.
Poniższy kod używa metody 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}");
}
}
Poniższy kod rejestruje wystąpienie konfiguracji, które MyOptions
wiąże się z:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
IOptionsMonitor
Poniższy kod rejestruje wystąpienie konfiguracji, które MyOptions
jest powiązane z.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddRazorPages();
}
W poniższym przykładzie użyto metody 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}");
}
}
W poprzednim kodzie domyślnie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Obsługa opcji nazwanych przy użyciu funkcji IConfigureNamedOptions
Nazwane opcje:
- Są przydatne, gdy wiele sekcji konfiguracji wiąże się z tymi samymi właściwościami.
- Uwzględnia wielkość liter.
Rozważ użycie następującego pliku appsettings.json
:
{
"TopItem": {
"Month": {
"Name": "Green Widget",
"Model": "GW46"
},
"Year": {
"Name": "Orange Gadget",
"Model": "OG35"
}
}
}
Zamiast tworzyć dwie klasy do powiązania TopItem:Month
i TopItem:Year
, dla każdej sekcji jest używana następująca klasa:
public class TopItemSettings
{
public const string Month = "Month";
public const string Year = "Year";
public string Name { get; set; }
public string Model { get; set; }
}
Poniższy kod konfiguruje nazwane opcje:
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();
}
Poniższy kod wyświetla nazwane opcje:
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" );
}
}
Wszystkie opcje są nazwane wystąpienia. IConfigureOptions<TOptions> wystąpienia są traktowane jako docelowe Options.DefaultName
wystąpienie, czyli string.Empty
. IConfigureNamedOptions<TOptions> implementuje IConfigureOptions<TOptions>również funkcję . Domyślna implementacja elementu IOptionsFactory<TOptions> ma logikę do użycia odpowiednio. Nazwana null
opcja służy do określania wartości docelowej wszystkich nazwanych wystąpień zamiast określonego nazwanego wystąpienia. ConfigureAll i PostConfigureAll stosować tę konwencję.
OptionsBuilder API
OptionsBuilder<TOptions> służy do konfigurowania TOptions
wystąpień. OptionsBuilder
Usprawnia tworzenie nazwanych opcji, ponieważ jest to tylko jeden parametr do początkowego AddOptions<TOptions>(string optionsName)
wywołania zamiast pojawiać się we wszystkich kolejnych wywołaniach. Opcje weryfikacji i ConfigureOptions
przeciążenia akceptujące zależności usługi są dostępne tylko za pośrednictwem .OptionsBuilder
OptionsBuilder
jest używany w sekcji Walidacja opcji.
Aby uzyskać informacje na temat dodawania repozytorium niestandardowego, zobacz Use AddOptions (Używanie polecenia AddOptions do konfigurowania repozytorium niestandardowego).
Konfigurowanie opcji przy użyciu usług DI
Dostęp do usług można uzyskać z iniekcji zależności podczas konfigurowania opcji na dwa sposoby:
Przekaż delegata konfiguracji do Configure adresu w dniu OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>
Zapewnia przeciążenia Configure , które umożliwiają korzystanie z maksymalnie pięciu usług do konfigurowania opcji:services.AddOptions<MyOptions>("optionalName") .Configure<Service1, Service2, Service3, Service4, Service5>( (o, s, s2, s3, s4, s5) => o.Property = DoSomethingWith(s, s2, s3, s4, s5));
Utwórz typ implementujący IConfigureOptions<TOptions> lub IConfigureNamedOptions<TOptions> rejestrujący typ jako usługę.
Zalecamy przekazanie delegata konfiguracji do Configureelementu , ponieważ tworzenie usługi jest bardziej złożone. Tworzenie typu jest równoważne z tym, co robi platforma podczas wywoływania metody Configure. Wywoływanie Configure powoduje zarejestrowanie przejściowego ogólnego IConfigureNamedOptions<TOptions>, który ma konstruktor, który akceptuje określone typy usług ogólnych.
Walidacja opcji
Walidacja opcji umożliwia zweryfikowanie wartości opcji.
Rozważ użycie następującego pliku appsettings.json
:
{
"MyConfig": {
"Key1": "My Key One",
"Key2": 10,
"Key3": 32
}
}
Następująca klasa wiąże się z sekcją "MyConfig"
konfiguracji i stosuje kilka DataAnnotations
reguł:
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; }
}
Następujący kod powoduje:
- Wywołania AddOptions w celu uzyskania elementu OptionsBuilder<TOptions> powiązanego z klasą
MyConfigOptions
. - Wywołania ValidateDataAnnotations w celu włączenia walidacji przy użyciu polecenia
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();
}
Metoda rozszerzenia jest definiowana ValidateDataAnnotations
w pakiecie NuGet Microsoft.Extensions.Options.DataAnnotations . W przypadku aplikacji internetowych korzystających z Microsoft.NET.Sdk.Web
zestawu SDK ten pakiet jest odwołuje się niejawnie z udostępnionej platformy.
Poniższy kod wyświetla wartości konfiguracji lub błędy walidacji:
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);
}
Poniższy kod stosuje bardziej złożoną regułę walidacji przy użyciu delegata:
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 na potrzeby złożonej weryfikacji
Następująca klasa implementuje IValidateOptions<TOptions>element :
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
umożliwia przeniesienie kodu weryfikacji z StartUp
klasy i do klasy.
Korzystając z powyższego kodu, walidacja jest włączona przy Startup.ConfigureServices
użyciu następującego kodu:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfigOptions>(Configuration.GetSection(
MyConfigOptions.MyConfig));
services.TryAddEnumerable(ServiceDescriptor.Singleton<IValidateOptions
<MyConfigOptions>, MyConfigValidation>());
services.AddControllersWithViews();
}
Opcje po konfiguracji
Ustaw konfigurację po konfiguracji za pomocą polecenia IPostConfigureOptions<TOptions>. Po zakończeniu konfiguracji wszystkie IConfigureOptions<TOptions> konfiguracje są uruchamiane:
services.PostConfigure<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
PostConfigure program jest dostępny do po skonfigurowaniu nazwanych opcji:
services.PostConfigure<MyOptions>("named_options_1", myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Użyj PostConfigureAll polecenia , aby po skonfigurowaniu wszystkich wystąpień konfiguracji:
services.PostConfigureAll<MyOptions>(myOptions =>
{
myOptions.Option1 = "post_configured_option1_value";
});
Uzyskiwanie dostępu do opcji podczas uruchamiania
IOptions<TOptions> i IOptionsMonitor<TOptions> może być używany w programie Startup.Configure
, ponieważ usługi są tworzone przed wykonaniem Configure
metody.
public void Configure(IApplicationBuilder app,
IOptionsMonitor<MyOptions> optionsAccessor)
{
var option1 = optionsAccessor.CurrentValue.Option1;
}
Nie używaj ani IOptions<TOptions> IOptionsMonitor<TOptions> w elemecie Startup.ConfigureServices
. Może istnieć niespójny stan opcji ze względu na kolejność rejestracji usług.
Options.ConfigurationExtensions pakiet NuGet
Pakiet Microsoft.Extensions.Options.ConfigurationExtensions jest niejawnie przywołyny w aplikacjach ASP.NET Core.