Встроенный механизм Dependency Injection в .NET основан на трех ключевых компонентах:
- Контейнер сервисов (
IServiceCollection
) — используется для регистрации зависимостей. - Провайдер сервисов (
IServiceProvider
) — отвечает за создание экземпляров зависимостей. - Область (Scope) — управляет временем жизни объектов.
Как работает регистрация зависимостей
При регистрации зависимостей в IServiceCollection
создаются правила создания экземпляров. Например:
var services = new ServiceCollection();
services.AddTransient<IMyService, MyService>();
services.AddSingleton<ILogger, ConsoleLogger>();
Этот код создает коллекцию сервисов, в которой определяется, как будет создаваться IMyService
и ILogger
.
Создание провайдера сервисов
После регистрации сервисов контейнер должен создать IServiceProvider
, который отвечает за разрешение зависимостей:
var provider = services.BuildServiceProvider();
var myService = provider.GetService<IMyService>();
Этот вызов возвращает экземпляр IMyService
, используя указанную стратегию создания.
Управление областью (Scope)
Scope позволяет управлять временем жизни зависимостей. В ASP.NET Core каждый HTTP-запрос создаёт новый Scope:
using (var scope = provider.CreateScope())
{
var scopedService = scope.ServiceProvider.GetRequiredService<IMyService>();
scopedService.Execute();
}
Жизненный цикл зависимостей
В .NET предусмотрены три основных типа времени жизни зависимостей:
- Transient — новый объект создаётся при каждом запросе.
services.AddTransient<IMyService, MyService>();
- Scoped — один объект на область (например, один HTTP-запрос).
services.AddScoped<IMyService, MyService>();
- Singleton — один объект на всё время работы приложения.
services.AddSingleton<IMyService, MyService>();
Как разрешаются зависимости
Когда IServiceProvider
запрашивает зависимость, он:
- Ищет зарегистрированную стратегию создания объекта.
- Определяет, существует ли уже экземпляр (если это Singleton или Scoped).
- Если экземпляра нет, создаёт его, разрешая все зависимости конструктора.
Пример класса с зависимостью:
public class MyService : IMyService
{
private readonly ILogger _logger;
public MyService(ILogger logger)
{
_logger = logger;
}
public void Execute()
{
_logger.Log("Executing MyService...");
}
}
Когда IServiceProvider
создаёт MyService
, он автоматически подставляет зарегистрированный ILogger
.