Как реализована инъекция зависимостей в ASP.NET Core
Что проверяют
Понимаете ли вы, как встроенный контейнер управляет временем жизни объектов, и видите ли ошибки, которые из этого вытекают. Самый частый уточняющий вопрос: что будет, если scoped-сервис попадёт в singleton.
Как отвечать
Контейнер встроен в платформу: Microsoft.Extensions.DependencyInjection. В современном шаблоне регистрация идёт в Program.cs через builder.Services. Класс Startup с ConfigureServices остался в старых проектах, шаблоны его больше не создают.
- Transient: новый экземпляр на каждое разрешение;
- Scoped: один экземпляр на область, в веб-приложении область это HTTP-запрос. Так по умолчанию регистрируется
DbContextчерезAddDbContext; - Singleton: один на всё приложение, обязан быть потокобезопасным.
Внедрение идёт через конструктор; контейнер выбирает конструктор с наибольшим числом параметров, которые умеет разрешить. В Minimal API сервисы приходят прямо параметрами обработчика, в контроллере отдельное действие может получить сервис через [FromServices]. Если зарегистрировать несколько реализаций, IEnumerable<T> вернёт все, а одиночное разрешение отдаст последнюю; TryAdd* не перезаписывает уже зарегистрированное.
Покажите знание современных возможностей: keyed services из .NET 8 (AddKeyedScoped, [FromKeyedServices("key")]) и IServiceScopeFactory.CreateAsyncScope() для BackgroundService, которому scoped-зависимость в конструктор передать нельзя.
Встроенный контейнер намеренно простой: нет внедрения в свойства, декораторов и сканирования сборок, для этого берут Scrutor или Autofac.
Где спотыкаются
Внедряют DbContext или другой scoped-сервис в singleton. Это captive dependency: один контекст живёт вечно и используется из разных потоков. Проверки ValidateScopes и ValidateOnBuild, включённые по умолчанию в Development, остановят приложение при старте, а в Production ошибка проявится уже под нагрузкой.
Внедряют IServiceProvider и достают из него всё вручную. Это service locator: зависимости класса больше не видны из конструктора.
Создают new HttpClient() на каждый запрос вместо IHttpClientFactory и получают исчерпание сокетов под нагрузкой.