Какой у ASP.NET Core DI контейнера lifetime и возможные варианты
Что проверяют
Знаете ли вы три времени жизни и, главное, что ломается при их неправильном сочетании. Почти наверняка следом спросят про captive dependency: что будет, если внедрить scoped-сервис в singleton, и как получить DbContext в фоновом сервисе.
Как отвечать
Встроенный контейнер Microsoft.Extensions.DependencyInjection даёт три варианта:
- Singleton (
AddSingleton): один экземпляр на всё время жизни корневого провайдера, то есть приложения. Обязан быть потокобезопасным, подходит для кэшей, клиентов без состояния, конфигурации; - Scoped (
AddScoped): один экземпляр на область. В ASP.NET Core область создаётся на каждый HTTP-запрос, в Blazor Server на соединение пользователя (circuit).DbContext, зарегистрированный черезAddDbContext, по умолчанию scoped; - Transient (
AddTransient): новый экземпляр при каждом разрешении, даже внутри одного запроса.
Правило совместимости: сервис может зависеть только от сервисов с таким же или более долгим временем жизни. Scoped внутри singleton «застревает» навсегда и становится общим для всех запросов. В окружении Development проверка областей включена по умолчанию, и такая регистрация приводит к исключению. В BackgroundService scoped-зависимости берут через IServiceScopeFactory.CreateScope() и освобождают область после работы.
Контейнер сам вызывает Dispose у созданных им объектов, когда закрывается их область. С .NET 8 есть keyed-сервисы (AddKeyedScoped и родственные) для нескольких реализаций одного интерфейса.
Где спотыкаются
Внедряют DbContext в singleton или в конструктор middleware. Контекст не потокобезопасен, и под нагрузкой появляются ошибки о параллельных операциях. Scoped-зависимости в middleware передаются параметром метода InvokeAsync.
Регистрируют disposable-сервис как transient и разрешают его из корневого провайдера. Экземпляры копятся до остановки приложения, это утечка памяти.
Считают, что scoped означает «один на поток». Область никак не связана с потоками: после await запрос может продолжиться в другом потоке с тем же экземпляром.