Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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:
- Reguła segregacji interfejsu (ISP) lub Hermetyzacja: scenariusze (klasy), które zależą od ustawień konfiguracji, zależą tylko od ustawień konfiguracji, z których korzystają.
- Separacja problemów: 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.
Powiązanie konfiguracji hierarchicznej
Preferowanym sposobem odczytywania powiązanych wartości konfiguracji jest użycie wzorca opcji. Wzorzec opcji jest możliwy za pośrednictwem interfejsu IOptions<TOptions>, gdzie parametr typu ogólnego TOptions jest ograniczony do class klasy. Można IOptions<TOptions> później przekazać za pomocą wstrzykiwania zależności. Aby uzyskać więcej informacji, zobacz Wstrzykiwanie zależności na platformie .NET.
Aby na przykład odczytać wyróżnione wartości konfiguracji z pliku appsettings.json :
{
"SecretKey": "Secret key value",
"TransientFaultHandlingOptions": {
"Enabled": true,
"AutoRetryDelay": "00:00:07"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Utwórz następującą klasę TransientFaultHandlingOptions:
public sealed class TransientFaultHandlingOptions
{
public bool Enabled { get; set; }
public TimeSpan AutoRetryDelay { get; set; }
}
W przypadku korzystania ze wzorca opcji klasa opcji:
- Musi być nie abstrakcyjny z publicznym konstruktorem bez parametrów
- Zawiera publiczne właściwości do odczytu i zapisu do powiązania (pola nie są powiązane)
Poniższy kod jest częścią pliku Program.cs C#i:
- Wywołanie elementu ConfigurationBinder.Bind w celu powiązania klasy
TransientFaultHandlingOptionsz sekcją"TransientFaultHandlingOptions". - Wyświetlenie danych konfiguracji .
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ConsoleJson.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Configuration.Sources.Clear();
IHostEnvironment env = builder.Environment;
builder.Configuration
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);
TransientFaultHandlingOptions options = new();
builder.Configuration.GetSection(nameof(TransientFaultHandlingOptions))
.Bind(options);
Console.WriteLine($"TransientFaultHandlingOptions.Enabled={options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={options.AutoRetryDelay}");
using IHost host = builder.Build();
// Application code should start here.
await host.RunAsync();
// <Output>
// Sample output:
W poprzednim kodzie sekcja "TransientFaultHandlingOptions" pliku konfiguracji JSON jest powiązana z wystąpieniem TransientFaultHandlingOptions. Spowoduje to nawodnienie właściwości obiektów języka C# odpowiednimi wartościami z konfiguracji.
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ą TransientFaultHandlingOptions:
var options =
builder.Configuration.GetSection(nameof(TransientFaultHandlingOptions))
.Get<TransientFaultHandlingOptions>();
Console.WriteLine($"TransientFaultHandlingOptions.Enabled={options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={options.AutoRetryDelay}");
W poprzednim kodzie ConfigurationBinder.Get<T> jest używany do uzyskania wystąpienia obiektu TransientFaultHandlingOptions z jego wartościami właściwości pochodzącymi z konfigurowania podstawowego.
Ważne
Klasa ConfigurationBinder uwidacznia kilka interfejsów API, takich jak .Bind(object instance) i .Get<T>(), które są nieograniczone do class. W przypadku korzystania z dowolnego interfejsu Opcje należy przestrzegać wyżej wymienionych ograniczeń klasy opcji.
Alternatywną metodą użycia wzorca opcji jest powiązanie "TransientFaultHandlingOptions" sekcji i dodanie jej do kontenera usług wstrzykiwania zależności. W poniższym kodzie element TransientFaultHandlingOptions jest dodawany do kontenera usługi przy użyciu Configure, a następnie jest tworzone jego powiązanie z konfiguracją:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.Configure<TransientFaultHandlingOptions>(
builder.Configuration.GetSection(
key: nameof(TransientFaultHandlingOptions)));
W poprzednim przykładzie builder jest przykładem HostApplicationBuilder.
Wskazówka
Parametr key jest nazwą sekcji konfiguracji do wyszukania. Nie musi być zgodna z nazwą typu, który go reprezentuje. Na przykład możesz mieć sekcję o nazwie "FaultHandling" i może być reprezentowana przez klasę TransientFaultHandlingOptions . W tym przypadku należy przekazać "FaultHandling" do funkcji GetSection. Operator nameof jest używany jako wygoda, gdy nazwana sekcja jest zgodna z typem, do którego odpowiada.
Przy użyciu poprzedniego kodu następujący kod odczytuje opcje położenia:
using Microsoft.Extensions.Options;
namespace ConsoleJson.Example;
public sealed class ExampleService(IOptions<TransientFaultHandlingOptions> options)
{
private readonly TransientFaultHandlingOptions _options = options.Value;
public void DisplayValues()
{
Console.WriteLine($"TransientFaultHandlingOptions.Enabled={_options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={_options.AutoRetryDelay}");
}
}
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji nie są odczytywane. Aby odczytać zmiany po uruchomieniu aplikacji, użyj funkcji IOptionsSnapshot lub IOptionsMonitor , aby monitorować zmiany w miarę ich występowania i odpowiednio reagować.
Interfejsy opcji
-
Nie obsługuje:
- Odczytywanie danych konfiguracji po uruchomieniu aplikacji.
- Opcje nazwane
- Rejestrowany jest jako Singleton i może być wstrzykiwany do dowolnego cyklu życia usługi.
- Jest przydatny w scenariuszach, w których opcje powinny być ponownie skompilowane w każdej rozdzielczości iniekcji, w okresach istnieniach o określonym zakresie lub przejściowym. Aby uzyskać więcej informacji, zobacz Używanie funkcji IOptionsSnapshot do odczytywania zaktualizowanych danych.
- Jest zarejestrowany jako Scoped i dlatego nie można go wstrzykiwać do usługi Singletona.
- Obsługuje nazwane opcje .
- Narzędzie służy do pobierania opcji oraz zarządzania powiadomieniami o opcjach dla wystąpień
TOptions. - Rejestrowany jest jako Singleton i może być wstrzykiwany do dowolnego cyklu życia usługi.
- Obsługuje:
- Zmienianie powiadomień
- Opcje nazwane
- Konfiguracja z możliwością ponownego ładowania
- Unieważnienie opcji selektywnych (IOptionsMonitorCache<TOptions>)
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> i najpierw uruchamia wszystkie konfiguracje, a następnie konfiguracje końcowe. 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> instancje opcji w monitorze, aby wartość została ponownie obliczona (TryRemove). Wartości można wprowadzić ręcznie za pomocą TryAdd. Metoda Clear jest używana, gdy wszystkie nazwane wystąpienia powinny być odtwarzane na żądanie.
IOptionsChangeTokenSource<TOptions> Służy do pobierania danych IChangeToken , które śledzą zmiany w wystąpieniu bazowym TOptions . Aby uzyskać więcej informacji na temat prymitywów tokenu zmiany, zobacz Powiadomienia o zmianach.
Korzyści z interfejsów opcji
Użycie ogólnego typu opakowania pozwala na uniezależnienie okresu istnienia opcji od kontenera DI (wstrzykiwania zależności). Interfejs IOptions<TOptions>.Value zapewnia warstwę abstrakcji, w tym ogólne ograniczenia, dla typu opcji. Oferuje to następujące korzyści:
- Ocena wystąpienia konfiguracji
Tjest odroczona do uzyskania dostępu do IOptions<TOptions>.Value, a nie podczas wstrzykiwania. Jest to ważne, ponieważ można skorzystaćTz opcji z różnych miejsc i wybrać semantykę czasu życia bez zmiany niczego oT. - Podczas rejestrowania opcji typu
T, nie trzeba jawnie rejestrować typuT. Jest to wygoda, gdy tworzysz bibliotekę z prostymi wartościami domyślnymi i nie chcesz wymuszać, aby obiekt wywołujący zarejestrował opcje w kontenerze DI z określonym okresem istnienia. - Z perspektywy interfejsu API, umożliwia nałożenie ograniczeń na typ
T(w tym przypadkuTjest ograniczone do typu referencyjnego).
Odczytywanie zaktualizowanych danych przy użyciu funkcji IOptionsSnapshot
Przy użyciu IOptionsSnapshot<TOptions>, opcje są obliczane tylko raz przy każdym żądaniu, gdy są dostępne, i są buforowane przez cały czas trwania żą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:
-
IOptionsMonitorto pojedyncza usługa , która w dowolnym momencie pobiera bieżące wartości opcji, co jest szczególnie przydatne w zależnościach pojedynczych. -
IOptionsSnapshotjest usługą o określonym zakresie i udostępnia migawkę opcji w momencieIOptionsSnapshot<T>konstruowania obiektu. Migawki ustawień są przeznaczone do użytku z zależnościami tymczasowymi i o określonym zakresie.
Poniższy kod używa metody IOptionsSnapshot<TOptions>.
using Microsoft.Extensions.Options;
namespace ConsoleJson.Example;
public sealed class ScopedService(IOptionsSnapshot<TransientFaultHandlingOptions> options)
{
private readonly TransientFaultHandlingOptions _options = options.Value;
public void DisplayValues()
{
Console.WriteLine($"TransientFaultHandlingOptions.Enabled={_options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={_options.AutoRetryDelay}");
}
}
Poniższy kod rejestruje instancję konfiguracji, która TransientFaultHandlingOptions powiązuje z:
builder.Services
.Configure<TransientFaultHandlingOptions>(
configurationRoot.GetSection(
nameof(TransientFaultHandlingOptions)));
W poprzednim kodzie Configure<TOptions> metoda służy do rejestrowania instancji konfiguracji, do której TOptions zostanie powiązany, i aktualizuje opcje po zmianie konfiguracji.
IOptionsMonitor
Typ IOptionsMonitor obsługuje powiadomienia o zmianie i umożliwia dynamiczne reagowanie aplikacji na zmiany źródła konfiguracji. Jest to przydatne, gdy trzeba reagować na zmiany w danych konfiguracji po uruchomieniu aplikacji. Powiadomienia o zmianach są obsługiwane tylko dla dostawców konfiguracji opartych na systemie plików, takich jak:
- Microsoft.Extensions.Configuration.Ini
- Microsoft.Extensions.Configuration.Json
- Microsoft.Extensions.Configuration.KeyPerFile
- Microsoft.Extensions.Configuration.UserSecrets
- Microsoft.Extensions.Configuration.Xml
Aby użyć monitora opcji, obiekty opcji są konfigurowane w taki sam sposób w sekcji konfiguracji.
builder.Services
.Configure<TransientFaultHandlingOptions>(
configurationRoot.GetSection(
nameof(TransientFaultHandlingOptions)));
W poniższym przykładzie użyto metody IOptionsMonitor<TOptions>:
using Microsoft.Extensions.Options;
namespace ConsoleJson.Example;
public sealed class MonitorService(IOptionsMonitor<TransientFaultHandlingOptions> monitor)
{
public void DisplayValues()
{
TransientFaultHandlingOptions options = monitor.CurrentValue;
Console.WriteLine($"TransientFaultHandlingOptions.Enabled={options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={options.AutoRetryDelay}");
}
}
W poprzednim kodzie zmiany w pliku konfiguracji JSON po uruchomieniu aplikacji są odczytywane.
Wskazówka
Niektóre systemy plików, takie jak kontenery platformy Docker i udziały sieciowe, mogą nie niezawodnie wysyłać powiadomień o zmianie. W przypadku korzystania z interfejsu IOptionsMonitor<TOptions> w tych środowiskach ustaw zmienną DOTNET_USE_POLLING_FILE_WATCHER środowiskową na 1 lub true sonduj system plików pod kątem zmian. Interwał sondowania zmian jest co cztery sekundy i nie można go konfigurować.
Aby uzyskać więcej informacji na temat kontenerów platformy Docker, zobacz Containerize a .NET app (Konteneryzowanie aplikacji .NET).
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ż następujący plik appsettings.json :
{
"Features": {
"Personalize": {
"Enabled": true,
"ApiKey": "aGEgaGEgeW91IHRob3VnaHQgdGhhdCB3YXMgcmVhbGx5IHNvbWV0aGluZw=="
},
"WeatherStation": {
"Enabled": true,
"ApiKey": "QXJlIHlvdSBhdHRlbXB0aW5nIHRvIGhhY2sgdXM/"
}
}
}
Zamiast tworzyć dwie klasy do powiązania Features:Personalize i Features:WeatherStation, dla każdej sekcji jest używana następująca klasa:
public class Features
{
public const string Personalize = nameof(Personalize);
public const string WeatherStation = nameof(WeatherStation);
public bool Enabled { get; set; }
public string ApiKey { get; set; }
}
Poniższy kod konfiguruje nazwane opcje:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
// Omitted for brevity...
builder.Services.Configure<Features>(
Features.Personalize,
builder.Configuration.GetSection("Features:Personalize"));
builder.Services.Configure<Features>(
Features.WeatherStation,
builder.Configuration.GetSection("Features:WeatherStation"));
Poniższy kod wyświetla nazwane opcje:
public sealed class Service
{
private readonly Features _personalizeFeature;
private readonly Features _weatherStationFeature;
public Service(IOptionsSnapshot<Features> namedOptionsAccessor)
{
_personalizeFeature = namedOptionsAccessor.Get(Features.Personalize);
_weatherStationFeature = namedOptionsAccessor.Get(Features.WeatherStation);
}
}
Wszystkie opcje są nazwanymi wystąpieniami.
IConfigureOptions<TOptions> wystąpienia są traktowane jako docelowe Options.DefaultName wystąpienie, czyli string.Empty.
IConfigureNamedOptions<TOptions> również implementuje IConfigureOptions<TOptions>. Domyślna implementacja elementu IOptionsFactory<TOptions> zawiera logikę pozwalającą na odpowiednie użycie. 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 stosują tę konwencję.
OptionsBuilder API
OptionsBuilder<TOptions> służy do konfigurowania TOptions wystąpień.
OptionsBuilder Ułatwia tworzenie nazwanych opcji, gdyż jest to tylko jeden parametr do początkowego wywołania AddOptions<TOptions>(string optionsName), zamiast pojawiać się w każdym z kolejnych wywołań. Opcje weryfikacji i przeciążenia ConfigureOptions, które akceptują zależności usług, są dostępne tylko za pośrednictwem OptionsBuilder.
OptionsBuilder jest używany w sekcji Walidacja opcji.
Konfigurowanie opcji przy użyciu usług DI
Podczas konfigurowania opcji można użyć wstrzykiwania zależności w celu uzyskania dostępu do zarejestrowanych usług i użyć ich do skonfigurowania opcji. Jest to przydatne, gdy konieczne jest uzyskanie dostępu do usług w celu skonfigurowania opcji. Dostęp do usług można uzyskać z di podczas konfigurowania opcji na dwa sposoby:
Przekaż delegata konfiguracji do Configure w OptionsBuilder<TOptions>.
OptionsBuilder<TOptions>zapewnia przeciążenia metody Configure, które umożliwiają korzystanie z maksymalnie pięciu usług do konfiguracji opcji.builder.Services .AddOptions<MyOptions>("optionalName") .Configure<ExampleService, ScopedService, MonitorService>( (options, es, ss, ms) => options.Property = DoSomethingWith(es, ss, ms));Utwórz typ implementujący IConfigureOptions<TOptions> lub IConfigureNamedOptions<TOptions> rejestrujący typ jako usługę.
Zaleca się przekazanie delegata konfiguracyjnego do Configure, ponieważ tworzenie usługi jest bardziej złożone. Tworzenie typu jest równoważne z tym, co platforma wykonuje podczas wywoływania konfiguracji. Wywoływanie polecenia Configure powoduje zarejestrowanie przejściowego typu ogólnego IConfigureNamedOptions<TOptions>, którego konstruktor akceptuje określone typy usług ogólnych.
Walidacja opcji
Walidacja opcji umożliwia zweryfikowanie wartości opcji.
Rozważ następujący plik appsettings.json :
{
"MyCustomSettingsSection": {
"SiteTitle": "Amazing docs from Awesome people!",
"Scale": 10,
"VerbosityLevel": 32
}
}
Następująca klasa wiąże się z sekcją "MyCustomSettingsSection" konfiguracji i stosuje kilka DataAnnotations reguł:
using System.ComponentModel.DataAnnotations;
namespace ConsoleJson.Example;
public sealed class SettingsOptions
{
public const string ConfigurationSectionName = "MyCustomSettingsSection";
[Required]
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
public required string SiteTitle { get; set; }
[Required]
[Range(0, 1_000,
ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public required int Scale { get; set; }
[Required]
public required int VerbosityLevel { get; set; }
}
W poprzedniej SettingsOptions klasie ConfigurationSectionName właściwość zawiera nazwę sekcji konfiguracji, z która ma być powiązana. W tym scenariuszu obiekt options zawiera nazwę sekcji konfiguracji.
Wskazówka
Nazwa sekcji konfiguracji jest niezależna od obiektu konfiguracji, z którą jest powiązanie. Innymi słowy, sekcja konfiguracji o nazwie "FooBarOptions" może być powiązana z obiektem opcji o nazwie ZedOptions. Chociaż często nazywa się je tak samo, nie jest to konieczne i może powodować konflikty nazw.
Następujący kod:
- Wywołuje AddOptions w celu uzyskania OptionsBuilder<TOptions>, który jest powiązany z klasą
SettingsOptions. - Wywołania ValidateDataAnnotations w celu włączenia walidacji przy użyciu
DataAnnotations.
builder.Services
.AddOptions<SettingsOptions>()
.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations();
Metoda rozszerzenia ValidateDataAnnotations jest zdefiniowana w pakiecie NuGet Microsoft.Extensions.Options.DataAnnotations.
Poniższy kod wyświetla wartości konfiguracji lub zgłasza błędy walidacji:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace ConsoleJson.Example;
public sealed class ValidationService
{
private readonly ILogger<ValidationService> _logger;
private readonly IOptions<SettingsOptions> _config;
public ValidationService(
ILogger<ValidationService> logger,
IOptions<SettingsOptions> config)
{
_config = config;
_logger = logger;
try
{
SettingsOptions options = _config.Value;
}
catch (OptionsValidationException ex)
{
foreach (string failure in ex.Failures)
{
_logger.LogError("Validation error: {FailureMessage}", failure);
}
}
}
}
Poniższy kod stosuje bardziej złożoną regułę walidacji przy użyciu delegata:
builder.Services
.AddOptions<SettingsOptions>()
.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Scale != 0)
{
return config.VerbosityLevel > config.Scale;
}
return true;
}, "VerbosityLevel must be > than Scale.");
Walidacja odbywa się w czasie wykonywania, ale można skonfigurować ją tak, aby występowała podczas uruchamiania, zestawiając wywołania do elementu ValidateOnStart.
builder.Services
.AddOptions<SettingsOptions>()
.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Scale != 0)
{
return config.VerbosityLevel > config.Scale;
}
return true;
}, "VerbosityLevel must be > than Scale.")
.ValidateOnStart();
Aby włączyć walidację przy uruchamianiu dla określonego typu opcji, użyj interfejsu AddOptionsWithValidateOnStart<TOptions>(IServiceCollection, String) API:
builder.Services
.AddOptionsWithValidateOnStart<SettingsOptions>()
.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Scale != 0)
{
return config.VerbosityLevel > config.Scale;
}
return true;
}, "VerbosityLevel must be > than Scale.");
IValidateOptions w przypadku złożonej weryfikacji
Następująca klasa implementuje IValidateOptions<TOptions>element :
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace ConsoleJson.Example;
sealed partial class ValidateSettingsOptions(
IConfiguration config)
: IValidateOptions<SettingsOptions>
{
public SettingsOptions? Settings { get; private set; } =
config.GetSection(SettingsOptions.ConfigurationSectionName)
.Get<SettingsOptions>();
public ValidateOptionsResult Validate(string? name, SettingsOptions options)
{
StringBuilder? failure = null;
if (!ValidationRegex().IsMatch(options.SiteTitle))
{
(failure ??= new()).AppendLine($"{options.SiteTitle} doesn't match RegEx");
}
if (options.Scale is < 0 or > 1_000)
{
(failure ??= new()).AppendLine($"{options.Scale} isn't within Range 0 - 1000");
}
if (Settings is { Scale: 0 } && Settings.VerbosityLevel <= Settings.Scale)
{
(failure ??= new()).AppendLine("VerbosityLevel must be > than Scale.");
}
return failure is not null
? ValidateOptionsResult.Fail(failure.ToString())
: ValidateOptionsResult.Success;
}
[GeneratedRegex("^[a-zA-Z''-'\\s]{1,40}$")]
private static partial Regex ValidationRegex();
}
IValidateOptions umożliwia przeniesienie kodu weryfikacji do klasy.
Uwaga
Ten przykładowy kod opiera się na pakiecie NuGet Microsoft.Extensions.Configuration.Json .
Korzystając z powyższego kodu, walidacja jest włączona podczas konfigurowania usług przy użyciu następującego kodu:
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
// Omitted for brevity...
builder.Services.Configure<SettingsOptions>(
builder.Configuration.GetSection(
SettingsOptions.ConfigurationSectionName));
builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton
<IValidateOptions<SettingsOptions>, ValidateSettingsOptions>());
Rekursywna walidacja za pomocą polecenia ValidateObjectMembers i ValidateEnumeratedItems
Domyślnie DataAnnotations walidacja weryfikuje tylko właściwości samej klasy options. Nie rekursywnie weryfikuje zagnieżdżonych obiektów ani elementów w kolekcjach. Aby włączyć walidację rekursywną, użyj atrybutów ValidateObjectMembersAttribute i ValidateEnumeratedItemsAttribute.
- Atrybut ValidateObjectMembersAttribute umożliwia rekursywną walidację zagnieżdżonych obiektów.
- Atrybut ValidateEnumeratedItemsAttribute umożliwia rekursywną walidację obiektów wyliczalnych.
Rozważ następujące zagnieżdżone klasy opcji:
public sealed class DatabaseOptions
{
[Required]
[MinLength(1)]
public string ConnectionString { get; set; } = string.Empty;
[Range(1, 100)]
public int MaxRetries { get; set; } = 3;
[Range(1, 300)]
public int TimeoutSeconds { get; set; } = 30;
}
public sealed class ServerOptions
{
[Required]
[RegularExpression(@"^[a-zA-Z0-9\-\.]+$")]
public string HostName { get; set; } = string.Empty;
[Required]
[Range(1, 65535)]
public int Port { get; set; }
}
public sealed class ApplicationOptionsWithAttribute
{
public const string ConfigurationSectionName = "ApplicationWithAttribute";
[Required]
public required string ApplicationName { get; set; }
// Validation recurses into DatabaseOptions.
[ValidateObjectMembers]
public DatabaseOptions Database { get; set; } = new();
// Validation recurses into each ServerOptions in the list.
[ValidateEnumeratedItems]
public List<ServerOptions> Servers { get; set; } = [];
}
W poprzednim kodzie właściwość Database jest obiektem zagnieżdżonym typu DatabaseOptions.
- Bez zastosowania atrybutu
[ValidateObjectMembers]do właściwościDatabaseOptions, atrybuty weryfikacji na jej właściwościach (na przykład na[Required]) nie byłyby oceniane. Po zastosowaniu[ValidateObjectMembers], walidacja jest również wykonywana rekurencyjnie w obrębie właściwościDatabasei weryfikuje jej członków zgodnie z ichDataAnnotationsatrybutami. - Bez zastosowania atrybutu
[ValidateEnumeratedItems]do właściwości kolekcjiServers, atrybuty walidacji poszczególnych elementówServerOptionsnie zostaną ocenione. Po zastosowaniu atrybutu[ValidateEnumeratedItems]każdyServerOptionselement na liście jest weryfikowany zgodnie z jegoDataAnnotationsatrybutami.
Wskazówka
Zarówno ValidateObjectMembersAttribute, jak i ValidateEnumeratedItemsAttribute pracują z generatorem źródłowym weryfikacji opcji w czasie kompilacji, aby zwiększyć wydajność. Aby uzyskać więcej informacji, zobacz Generowanie źródła walidacji opcji czasu kompilacji.
Opcje po konfiguracji
Ustaw konfigurację końcową używając IPostConfigureOptions<TOptions>. Po zakończeniu konfiguracji, działania pokonfiguracyjne IConfigureOptions<TOptions> mogą być przydatne w scenariuszach, w których trzeba nadpisać konfigurację:
builder.Services.PostConfigure<CustomOptions>(customOptions =>
{
customOptions.Option1 = "post_configured_option1_value";
});
PostConfigure jest dostępny do konfiguracji po ustawieniu opcji o nazwach:
builder.Services.PostConfigure<CustomOptions>("named_options_1", customOptions =>
{
customOptions.Option1 = "post_configured_option1_value";
});
Użyj PostConfigureAll, aby skonfigurować wszystkie wystąpienia konfiguracji.
builder.Services.PostConfigureAll<CustomOptions>(customOptions =>
{
customOptions.Option1 = "post_configured_option1_value";
});