Options pattern: IOptions, IOptionsSnapshot, IOptionsMonitor, валидация
↑ 3.1 Хост, конфигурация и middleware pipeline · ← Предыдущая · → Следующая
Зачем это на собесе
Разница между тремя интерфейсами Options и умение включить валидацию на старте — типичный вопрос на middle.
Объяснение
Options pattern привязывает секцию конфигурации к типизированному классу.
public class JwtOptions
{
public const string Section = "Jwt";
[Required] public string Issuer { get; init; } = "";
[Required, MinLength(32)] public string Key { get; init; } = "";
[Range(1, 1440)] public int LifetimeMinutes { get; init; } = 60;
}
builder.Services
.AddOptions<JwtOptions>()
.Bind(builder.Configuration.GetSection(JwtOptions.Section))
.ValidateDataAnnotations()
.ValidateOnStart();| Интерфейс | Время жизни | Обновление | Когда |
|---|---|---|---|
IOptions<T> | singleton | не обновляется | статичные настройки |
IOptionsSnapshot<T> | scoped | обновляется на каждый запрос | веб-запросы с перечитыванием |
IOptionsMonitor<T> | singleton | обновляется, есть OnChange | фоновые сервисы, singleton-ы |
public class TokenService(IOptionsMonitor<JwtOptions> opts)
{
public string Issuer => opts.CurrentValue.Issuer; // всегда свежее значение
}Именованные options и ValidateOnStart
Configure<T>("name", ...) и IOptionsMonitor<T>.Get("name") позволяют держать несколько конфигураций одного типа (несколько клиентов, провайдеров). ValidateOnStart() роняет приложение при старте с понятной ошибкой, а не при первом обращении.
Нюансы и подводные камни
IOptionsSnapshotнельзя внедрять в singleton (scoped внутри singleton — captive dependency).- Для
init/required-свойств и привязки используйтеBindConfigurationилиBind. - Без
ValidateOnStartошибки конфигурации проявятся на проде при первом запросе. - Сложную валидацию делайте через
IValidateOptions<T>. - Не внедряйте
IConfigurationв бизнес-код: сложно тестировать и нет типов.
Практика
- Оформите секцию
Smtpкак Options с валидацией иValidateOnStart. - Измените appsettings на лету и проследите, как ведут себя Snapshot и Monitor.
- Напишите
IValidateOptions<T>с зависимой проверкой (еслиUseTls, тоPortне 25).
Вопросы с ответами
Чем IOptions, IOptionsSnapshot и IOptionsMonitor отличаются?
IOptionsчитается один раз и живёт как singleton;IOptionsSnapshot— scoped, перечитывается на запрос;IOptionsMonitor— singleton, всегда возвращает актуальное значение и уведомляет об изменениях.
Зачем ValidateOnStart?
Чтобы ошибки конфигурации приводили к падению при запуске (fail fast), а не в рантайме.
Как использовать IOptionsSnapshot в singleton?
Нельзя напрямую; используйте
IOptionsMonitorлибо создавайте scope.