Время жизни: Singleton, Scoped, Transient
↑ 3.2 Dependency Injection · ← Предыдущая · → Следующая
Зачем это на собесе
Обязательный вопрос. Ждут не определений, а примеров: что сделать 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>();Правила зависимостей
Сервис может зависеть от сервисов с таким же или более длинным временем жизни:
| Кто \ От кого | Singleton | Scoped | Transient |
|---|---|---|---|
| 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, помогая находить ошибки.
Практика
- Выведите
GetHashCode()сервисов в трёх временах жизни на нескольких запросах. - Внедрите scoped в singleton и посмотрите ошибку валидации.
- Создайте scope в
BackgroundService.
Вопросы с ответами
Что значит scoped в ASP.NET Core?
Один экземпляр на HTTP-запрос: контейнер создаёт scope на запрос и освобождает его в конце.
Можно ли внедрить scoped в singleton?
Нет, получится captive dependency: scoped-объект «застрянет» на время жизни singleton.
Что выбрать по умолчанию?
Для stateless-сервисов подходят все; Singleton — если сервис потокобезопасен и дорог в создании; Scoped — если хранит контекст запроса.