Время жизни: Singleton, Scoped, Transient

↑ 3.2 Dependency Injection · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Обязательный вопрос. Ждут не определений, а примеров: что сделать singleton-ом, что scoped-ом и что ломается при ошибке.

Объяснение

Время жизниЭкземпляровТипичное применение
Singletonодин на приложениекэш, фабрики, HttpClient-обёртки, конфигурация, без состояния
Scopedодин на scope (в вебе — на HTTP-запрос)DbContext, unit of work, контекст пользователя
Transientновый при каждом запросе к контейнерулёгкие stateless сервисы
builder.Services.AddSingleton<IClock, SystemClock>();
builder.Services.AddScoped<AppDbContext>();
builder.Services.AddTransient<IEmailBuilder, EmailBuilder>();

Правила зависимостей

Сервис может зависеть от сервисов с таким же или более длинным временем жизни:

Кто \ От когоSingletonScopedTransient
Singletonданет (captive)осторожно
Scopedдадада
Transientдадада

Scope вне запроса

В фоновом сервисе scope нужно создавать вручную:

using var scope = scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

Освобождение

Контейнер вызывает Dispose/DisposeAsync у объектов, которые сам создал. Экземпляр, зарегистрированный готовым (AddSingleton(instance)), контейнер не освобождает.

Нюансы и подводные камни

  • Singleton со статичным состоянием обязан быть потокобезопасным.
  • Transient IDisposable в singleton накапливается до конца приложения (утечка).
  • DbContext — не потокобезопасный, только scoped.
  • В Development включается ValidateScopes и ValidateOnBuild, помогая находить ошибки.

Практика

  1. Выведите GetHashCode() сервисов в трёх временах жизни на нескольких запросах.
  2. Внедрите scoped в singleton и посмотрите ошибку валидации.
  3. Создайте scope в BackgroundService.

Вопросы с ответами

Связанные темы