Ответ
В ASP.NET Core Dependency Injection контейнер поддерживает следующие основные виды регистрации (времена жизни):
-
Transient — создаётся новый экземпляр при каждом запросе зависимости. Подходит для лёгких, не имеющих состояния сервисов.
services.AddTransient<IService, ServiceImplementation>(); -
Scoped — один экземпляр создаётся в рамках одной области видимости (scope). В веб-приложении область видимости обычно соответствует одному HTTP-запросу.
services.AddScoped<IService, ServiceImplementation>(); -
Singleton — один экземпляр на всё время работы приложения. Используйте с осторожностью, учитывая потокобезопасность.
services.AddSingleton<IService, ServiceImplementation>();
Также доступны дополнительные способы регистрации:
- Регистрация экземпляра (Instance): Предоставление уже созданного объекта.
services.AddSingleton<IService>(existingInstance); - Регистрация через фабрику (Factory): Позволяет управлять логикой создания экземпляра.
services.AddTransient<IService>(serviceProvider => new ServiceImplementation(serviceProvider.GetRequiredService<ILogger>()) );
Сторонние контейнеры (например, Autofac) часто расширяют эти возможности, добавляя Keyed/Named регистрации (регистрация нескольких реализаций под разными ключами) и поддержку декораторов.
Ответ 18+ 🔞
Давай разберём эту тему про контейнер в ASP.NET Core, а то некоторые думают, что это какая-то магия, а на деле — просто логика, как в жизни.
Смотри, представь, что контейнер зависимостей — это такой умный склад. Ты ему говоришь: «Слушай, если кто-то попросит молоток, дай ему вот этот конкретный молоток» или «...сделай новый» или «...дай тот, который уже в работе у этого прораба». Вот и вся философия.
Основные три варианта, как этот склад может отдавать твои «сервисы»:
-
Transient (Временный) Каждый раз, когда кто-то просит — новый, свеженький, с иголочки. Заказали 100 раз — получили 100 разных экземпляров. Идеально для чего-то лёгкого и без состояния, что не жалко выкинуть после использования.
services.AddTransient<IService, ServiceImplementation>();Это как одноразовые стаканчики на вечеринке. Взял, выпил, смял, выбросил. Никакой привязки.
-
Scoped (В рамках области) Вот это уже интереснее. В рамках одной «области» (scope) — один и тот же экземпляр. В веб-приложении одна область — это обычно один HTTP-запрос. Всё, что происходит внутри обработки этого запроса, получает один и тот же экземпляр.
services.AddScoped<IService, ServiceImplementation>();Представь бригаду рабочих на одном объекте (запросе). Им выдают один общий перфоратор (scoped-сервис) на всех. Пока объект не сдан, они им и пользуются. Новый объект (запрос) — новая бригада и новый перфоратор.
-
Singleton (Одиночка) Один на всё приложение. Создали при старте — и он живёт, пока приложение не вырубят. Осторожно, блядь! Если он не потокобезопасный, будет пиздец и танцы с бубном при конкурентных запросах.
services.AddSingleton<IService, ServiceImplementation>();Это как общий чайник на весь офис. Один, навсегда. Все из него пьют. Если он сломается — всем пизда, кипятка не будет.
А ещё есть парочка хитрых приёмов, чтобы не просто тыкать в контейнер типы:
-
Регистрация готового экземпляра (Instance): «На, держи вот этот конкретный, уже настроенный и готовый к бою объект. Его и отдавай, когда спросят».
services.AddSingleton<IService>(existingInstance);Сам сделал, сам и отдал в управление. Удобно, когда объект создаётся нестандартно, а потом хочешь, чтобы он жил как singleton.
-
Регистрация через фабрику (Factory): «Слушай, друг, когда будут просить эту зависимость, сделай вот по такому рецепту. Сам решай, что и откуда взять для её создания».
services.AddTransient<IService>(serviceProvider => new ServiceImplementation(serviceProvider.GetRequiredService<ILogger>()) );Полная власть над процессом создания. Хочешь — логику добавь, хочешь — другие зависимости из того же контейнера вытащи (
serviceProvider). Мощная штука.
Ну и про сторонние контейнеры, вроде Autofac. Стандартный контейнер из коробки — он как базовый набор инструментов: молоток, отвёртка, пассатижи. А эти ребята привозят целый фургон с профессиональным оборудованием.
Они умеют в Keyed/Named регистрации — это когда у тебя несколько реализаций одного интерфейса, и ты говоришь: «Эту реализацию отдавай по ключу "A", а эту — по ключу "B"». Стандартный контейнер так из коробки не умеет, приходится выкручиваться.
И декораторы — это вообще красота. Можно обернуть сервис в другой сервис, как матрёшку, добавляя логирование, кэширование или ещё какую хуйню, не лезя в исходный код. Очень элегантный паттерн, который стандартный контейнер поддерживает с трудом.
Короче, суть в чём: выбрал правильное время жизни — избежал тонких и ебучих багов с состоянием. Воспользовался фабрикой — получил контроль. Нужна продвинутая фича — подключай Autofac или другой зверь-контейнер. Главное — понимать, что ты делаешь, а не просто копипастить.