Ответ
Это три основных времени жизни (lifetime) сервисов, регистрируемых в DI-контейнере ASP.NET Core. Выбор влияет на производительность, потребление памяти и корректность работы приложения.
1. Transient (AddTransient)
- Поведение: Создаётся новый экземпляр сервиса каждый раз, когда он запрашивается из контейнера.
- Использование: Для лёгких, статусных (stateful) или дорогих в создании сервисов, которые не должны разделяться.
- Пример:
services.AddTransient<IMyService, MyService>(); // В контроллере дважды запрошен IMyService: var service1 = scope.ServiceProvider.GetService<IMyService>(); // Новый экземпляр var service2 = scope.ServiceProvider.GetService<IMyService>(); // Ещё один новый экземпляр
2. Scoped (AddScoped)
- Поведение: Создаётся один экземпляр на область видимости (scope). В веб-приложении область видимости по умолчанию соответствует одному HTTP-запросу.
- Использование: Идеально для сервисов, которым необходим контекст в рамках одной операции (например,
DbContextEntity Framework Core). - Пример:
services.AddScoped<IMyRepository, MyRepository>(); // В рамках одного запроса несколько классов получат один и тот же экземпляр репозитория.
3. Singleton (AddSingleton)
- Поведение: Создаётся один экземпляр на всё время жизни приложения (с момента первого разрешения).
- Использование: Для статусных (stateless), потокобезопасных сервисов, таких как кэш в памяти, логгеры или клиенты для внешних API.
- Пример:
services.AddSingleton<IMemoryCache, MemoryCache>();
Критические правила и best practices:
- Потокобезопасность: Singleton-сервисы должны быть потокобезопасными, так как к ним возможен одновременный доступ из множества потоков.
- Зависимости (Captive Dependency): Никогда не инжектируйте Scoped-сервис в Singleton. Singleton «захватит» Scoped-сервис, и тот будет жить вечно, что может привести к утечке памяти (например,
DbContextне будет освобождать соединения). - Производительность: Transient для тяжёлых объектов может создать нагрузку, а Singleton для объектов с состоянием — ошибки.