Для чего используется интерфейс IServiceProvider и как он применяется?
Что проверяют
Понимаете ли вы, как устроен встроенный DI-контейнер .NET и чем внедрение через конструктор отличается от ручного обращения к контейнеру. Следом почти всегда спрашивают про Service Locator и про то, как получить scoped-сервис из синглтона.
Как отвечать
System.IServiceProvider это интерфейс с одним методом object? GetService(Type serviceType), который возвращает null, если сервис не зарегистрирован. Всё удобное лежит в методах расширения из Microsoft.Extensions.DependencyInjection:
GetService<T>(): вернётnullпри отсутствии регистрации;GetRequiredService<T>(): броситInvalidOperationException, в прикладном коде обычно нужен именно он;GetServices<T>(): все реализации интерфейса, если их зарегистрировано несколько;CreateScope()иCreateAsyncScope(): новая область для scoped-сервисов;GetKeyedService<T>(key): keyed-сервисы, появились в .NET 8.
Контейнер строится из IServiceCollection через BuildServiceProvider(), в ASP.NET Core это делает хост. Корневой провайдер доступен как app.Services, а на каждый запрос создаётся область, её провайдер лежит в HttpContext.RequestServices. Время жизни задаётся при регистрации (AddSingleton, AddScoped, AddTransient), и провайдер сам вызывает Dispose у созданных им объектов, когда закрывается их область.
Прямой вызов оправдан в фабриках, в инфраструктурном коде и в BackgroundService, которому нужен DbContext: синглтон создаёт область на каждую итерацию и берёт сервис из неё. В обычных классах зависимости принимаются через конструктор.
Где спотыкаются
Внедряют IServiceProvider в сервисы и достают всё вручную. Это Service Locator: зависимости не видны из сигнатуры, а ошибка регистрации всплывает только в рантайме.
Резолвят scoped-сервис из корневого провайдера. В режиме разработки с ValidateScopes это исключение, а в продакшене DbContext фактически живёт как синглтон со всеми последствиями.
Вызывают BuildServiceProvider() посреди конфигурации сервисов. Получается второй контейнер с дублями синглтонов, анализатор ASP.NET Core отдельно предупреждает об этом.