Dependency Injection контейнер для C# (.NET): полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Dependency Injection контейнер для C# (.NET): полное руководство

Dependency Injection (DI) — это один из ключевых паттернов проектирования в современной разработке на C# и .NET. Он позволяет создавать слабосвязанные, тестируемые и легко поддерживаемые приложения. В этой статье мы подробно рассмотрим, что такое Dependency Injection контейнер, как его использовать в .NET, и приведем практические примеры кода.

Что такое Dependency Injection?

Dependency Injection (внедрение зависимостей) — это паттерн, при котором объект получает свои зависимости извне, а не создает их самостоятельно. Это позволяет:

  • Уменьшить связанность между компонентами
  • Упростить тестирование (возможность подмены зависимостей моками)
  • Повысить гибкость и переиспользуемость кода
  • Централизовать управление жизненным циклом объектов

Что такое DI контейнер?

DI контейнер (или IoC контейнер) — это библиотека, которая автоматизирует процесс внедрения зависимостей. Он берет на себя ответственность за создание объектов и управление их временем жизни. В .NET основным DI контейнером является Microsoft.Extensions.DependencyInjection, который встроен в ASP.NET Core и может использоваться в любых .NET приложениях.

Основные понятия DI контейнера

DI контейнер оперирует следующими понятиями:

  • Регистрация служб — процесс добавления типов и их реализаций в контейнер
  • Разрешение зависимостей — автоматическое создание объектов с их зависимостями
  • Время жизни служб — определяет, как долго существует объект (Transient, Scoped, Singleton)

Настройка DI контейнера в .NET

Для начала работы с DI контейнером необходимо установить пакет Microsoft.Extensions.DependencyInjection. Это можно сделать через NuGet:

dotnet add package Microsoft.Extensions.DependencyInjection

После установки создается экземпляр ServiceCollection, в котором регистрируются все зависимости:

using Microsoft.Extensions.DependencyInjection;

// Создаем коллекцию служб
var services = new ServiceCollection();

// Регистрируем службы
services.AddTransient<ILogger, ConsoleLogger>();
services.AddScoped<IUserRepository, UserRepository>();
services.AddSingleton<IConfiguration, AppConfiguration>();

// Строим провайдер служб
var serviceProvider = services.BuildServiceProvider();

Пример использования DI контейнера

Рассмотрим реальный пример: приложение для управления пользователями. Создадим интерфейсы и их реализации:

public interface IUserRepository
{
    User GetUser(int id);
}

public interface IEmailService
{
    void SendEmail(string to, string subject);
}

public class UserRepository : IUserRepository
{
    public User GetUser(int id)
    {
        // Логика получения пользователя из БД
        return new User { Id = id, Name = "John Doe" };
    }
}

public class EmailService : IEmailService
{
    public void SendEmail(string to, string subject)
    {
        Console.WriteLine($"Email sent to {to}: {subject}");
    }
}

Теперь создадим класс UserService, который использует эти зависимости:

public class UserService
{
    private readonly IUserRepository _userRepository;
    private readonly IEmailService _emailService;

    public UserService(IUserRepository userRepository, IEmailService emailService)
    {
        _userRepository = userRepository;
        _emailService = emailService;
    }

    public void NotifyUser(int userId)
    {
        var user = _userRepository.GetUser(userId);
        _emailService.SendEmail(user.Email, "Welcome!");
    }
}

Регистрируем все в DI контейнере и используем:

var services = new ServiceCollection();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<IEmailService, EmailService>();
services.AddTransient<UserService>();

var serviceProvider = services.BuildServiceProvider();
var userService = serviceProvider.GetService<UserService>();
userService.NotifyUser(1);

Время жизни служб (Lifetimes)

DI контейнер поддерживает три основных времени жизни:

  • Transient — новый экземпляр создается каждый раз при запросе. Идеально для легковесных, не имеющих состояния служб.
  • Scoped — один экземпляр на область (например, на HTTP-запрос в ASP.NET Core). Подходит для контекстов БД (DbContext).
  • Singleton — один экземпляр на все приложение. Используется для кэшей, конфигураций, логгеров.
// Transient: каждый запрос создает новый экземпляр
services.AddTransient<IMyService, MyService>();

// Scoped: один экземпляр на область (например, на запрос)
services.AddScoped<IMyService                    

Рекомендаций