Назови варианты времени жизни объекта в DI контейнере
Что проверяют
Знаете ли вы три режима и, главное, чем грозит их смешивание. Названия вспоминают почти все, а вопрос на самом деле про захваченные зависимости, потокобезопасность и освобождение ресурсов.
Как отвечать
В стандартном контейнере три варианта:
Transient: новый экземпляр на каждое разрешение. Для лёгких объектов без состояния;Scoped: один экземпляр на область. В веб-приложении область создаётся на каждый HTTP-запрос, поэтому так регистрируют контекст базы данных и всё, что должно жить ровно один запрос;Singleton: один на всё приложение, живёт в корневом провайдере. Для кэшей, конфигурации, долгоживущих клиентов.
Дальше главное правило: зависимость с более коротким временем жизни нельзя внедрять в объект с более длинным. Scoped внутри Singleton захватывается навсегда, и контекст базы превращается в общий на всё приложение объект. Проверка областей при старте ловит это в отладочной среде, но её иногда отключают.
Обход для фоновых служб, которые сами синглтоны: брать IServiceScopeFactory и создавать область на каждую итерацию, а внутри неё уже разрешать зависимости.
Полезно добавить, что словарь зависит от контейнера: в Autofac те же режимы называются InstancePerDependency, InstancePerLifetimeScope и SingleInstance, плюс есть область по тегу.
Где спотыкаются
Считают Scoped временем жизни пользовательской сессии. Область про сессию ничего не знает, это просто время жизни IServiceScope, и в консольном приложении или в тесте её создают руками.
Второе: забывают про освобождение ресурсов. Контейнер освобождает то, что создал сам, и Transient-объект с Dispose, разрешённый из корневого провайдера, доживёт до остановки приложения. Получается утечка, которую видно только под нагрузкой.
Третье: регистрируют синглтоном объект с изменяемым состоянием и не думают про потоки. Синглтон видят все запросы одновременно, а контекст базы и многие клиентские обёртки к параллельному использованию не готовы.