Как прокидывать зависимости в глубоко вложенные компоненты с помощью DI контейнеров
Что проверяют
Понимаете ли вы, что с контейнером зависимости никуда «прокидывать» не нужно: каждый класс просит в конструкторе только своё, а граф собирает контейнер. Следом спрашивают про время жизни сервисов и про объекты, которые создаются уже во время работы программы.
Как отвечать
- композиционный корень: все регистрации в одном месте, в
Program.csчерезbuilder.Services. ЕслиOrderControllerзависит отOrderService, а тот отIPaymentGatewayиIOrderRepository, контроллер про шлюз не знает; - время жизни:
AddTransientдаёт новый экземпляр на каждое разрешение,AddScopedодин на HTTP-запрос,AddSingletonодин на приложение.DbContextрегистрируется как scoped; - captive dependency: singleton, получивший scoped-сервис в конструкторе, держит его вечно. В среде Development ASP.NET Core включает
ValidateScopes, и такой граф падает с исключением. Без проверки одинDbContextделят все запросы; - объекты времени выполнения: если глубоко внутри нужно создать объект с параметрами, известными только сейчас, внедряйте фабрику: свой
IOrderFactoryили зарегистрированныйFunc<T>. ВBackgroundServicescoped-зависимости берут черезIServiceScopeFactory.CreateScope(). Для смеси runtime-аргументов и сервисов естьActivatorUtilities.CreateInstance; - конфигурация: не передавайте настройки вниз по цепочке, регистрируйте
IOptions<T>; - несколько реализаций: с .NET 8 есть keyed services,
AddKeyedScopedи атрибут[FromKeyedServices].
В Blazor то же самое: @inject в нужном компоненте, а не параметр через пять уровней. Каскадные параметры оставьте для темы или текущего пользователя.
Где спотыкаются
Внедряют IServiceProvider и зовут GetService где попало. Это service locator: зависимости перестают быть видны в конструкторе, а тесты превращаются в настройку контейнера.
Регистрируют DbContext или сервис с состоянием как singleton «для производительности». Данные начинают течь между пользователями, а EF Core падает на параллельных операциях с одним контекстом.
Терпят конструктор на десять параметров. Контейнер его соберёт, но это сигнал, что класс делает слишком много и его пора делить.