Ответ
В ASP.NET Core Dependency Injection (DI) время жизни сервиса определяет, как долго контейнер будет хранить его экземпляр. Правильный выбор критичен для производительности и корректности работы приложения.
Три основных времени жизни:
-
Transient – создается новый экземпляр для каждого запроса к контейнеру.
- Используйте для: легковесных, stateless сервисов.
- Проблема: может создать нагрузку, если сервис тяжелый и запрашивается часто.
-
Scoped – один экземпляр создается на область видимости (scope), например, на один HTTP-запрос.
- Используйте для: сервисов, которым нужен контекст в рамках операции (например,
DbContextв Entity Framework Core). - Проблема: попытка разрешить Scoped-сервис из Singleton или вне области видимости вызовет исключение.
- Используйте для: сервисов, которым нужен контекст в рамках операции (например,
-
Singleton – один экземпляр на все время работы приложения.
- Используйте для: stateless сервисов, кэшей, конфигураций.
- Проблема: если Singleton зависит от Scoped-сервиса, этот Scoped-сервис неявно становится Singleton, что может привести к утечке памяти или некорректным данным (например, один
DbContextна все запросы).
Пример проблемы и решения:
// ПРОБЛЕМА: Singleton, зависящий от Scoped-сервиса.
public class CacheService
{
private readonly AppDbContext _dbContext; // Scoped-сервис!
public CacheService(AppDbContext dbContext) => _dbContext = dbContext;
// _dbContext будет захвачен и жить вечно, вызывая утечку.
}
// РЕШЕНИЕ: Внедрять IServiceScopeFactory и создавать область видимости явно.
public class CacheService
{
private readonly IServiceScopeFactory _scopeFactory;
public CacheService(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;
public async Task<string> GetCachedDataAsync()
{
using (var scope = _scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// Работаем с dbContext в его собственной области видимости.
return await dbContext.Data.FindAsync(1);
}
}
}
Ключевые правила:
- Scoped-сервисы не должны быть разрешены из Singleton-сервисов напрямую.
- Transient-сервисы не должны внедряться в Singleton-сервисы, если они не предназначены для этого, так как тоже станут де-факто Singleton.
- Всегда проверяйте граф зависимостей на предмет captive dependencies (захвата зависимостей с более коротким временем жизни).