← Назад к вопросам

Приведи пример использования DI в ASP.NET Core

2.0 Middle🔥 171 комментариев
#ASP.NET и Web API

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример внедрения зависимостей в ASP.NET Core

Внедрение зависимостей (Dependency Injection, DI) — это фундаментальный паттерн в ASP.NET Core, который позволяет создавать слабосвязанные, тестируемые и поддерживаемые приложения. Рассмотрим полный пример с регистрацией сервисов, их внедрением и использованием.

1. Создание интерфейсов и сервисов

Сначала определим контракты и их реализации:

// Интерфейс для службы работы с email
public interface IEmailService
{
    Task SendEmailAsync(string to, string subject, string body);
}

// Реализация службы email
public class SmtpEmailService : IEmailService
{
    private readonly ILogger<SmtpEmailService> _logger;
    
    public SmtpEmailService(ILogger<SmtpEmailService> logger)
    {
        _logger = logger;
    }
    
    public async Task SendEmailAsync(string to, string subject, string body)
    {
        _logger.LogInformation($"Отправка email на {to}: {subject}");
        // Реальная логика отправки через SMTP
        await Task.Delay(100); // Имитация отправки
    }
}

// Интерфейс репозитория пользователей
public interface IUserRepository
{
    Task<User> GetUserByIdAsync(int id);
    Task AddUserAsync(User user);
}

// Модель пользователя
public class User
{
    public int Id { get; set; }
    public string Email { get; set; }
    public string Name { get; set; }
}

// Реализация репозитория
public class UserRepository : IUserRepository
{
    private readonly List<User> _users = new();
    
    public Task<User> GetUserByIdAsync(int id)
    {
        var user = _users.FirstOrDefault(u => u.Id == id);
        return Task.FromResult(user);
    }
    
    public Task AddUserAsync(User user)
    {
        _users.Add(user);
        return Task.CompletedTask;
    }
}

2. Сервис бизнес-логики с внедрением зависимостей

public class UserService
{
    private readonly IUserRepository _userRepository;
    private readonly IEmailService _emailService;
    private readonly ILogger<UserService> _logger;
    
    // Конструктор с внедрением зависимостей
    public UserService(
        IUserRepository userRepository,
        IEmailService emailService,
        ILogger<UserService> logger)
    {
        _userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository));
        _emailService = emailService ?? throw new ArgumentNullException(nameof(emailService));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
    
    public async Task RegisterUserAsync(User user)
    {
        _logger.LogInformation($"Регистрация пользователя: {user.Name}");
        
        // Сохраняем пользователя
        await _userRepository.AddUserAsync(user);
        
        // Отправляем приветственное письмо
        await _emailService.SendEmailAsync(
            user.Email,
            "Добро пожаловать!",
            $"Уважаемый {user.Name}, спасибо за регистрацию!");
    }
}

3. Конфигурация DI контейнера в Program.cs

var builder = WebApplication.CreateBuilder(args);

// Регистрация сервисов в DI контейнере
builder.Services.AddScoped<IEmailService, SmtpEmailService>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<UserService>();

// Регистрация контроллера
builder.Services.AddControllers();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

4. Использование в контроллере

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly UserService _userService;
    private readonly ILogger<UsersController> _logger;
    
    // DI автоматически внедрит зависимости через конструктор
    public UsersController(UserService userService, ILogger<UsersController> logger)
    {
        _userService = userService;
        _logger = logger;
    }
    
    [HttpPost]
    public async Task<IActionResult> CreateUser([FromBody] User user)
    {
        _logger.LogInformation("Создание нового пользователя");
        
        try
        {
            await _userService.RegisterUserAsync(user);
            return Ok(new { Message = "Пользователь успешно создан" });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Ошибка при создании пользователя");
            return StatusCode(500, "Внутренняя ошибка сервера");
        }
    }
}

5. Варианты времени жизни сервисов

// В Program.cs можно настроить разные времена жизни:
builder.Services.AddTransient<ITransientService, TransientService>(); // Новый экземпляр каждый раз
builder.Services.AddScoped<IScopedService, ScopedService>(); // Один экземпляр на запрос
builder.Services.AddSingleton<ISingletonService, SingletonService>(); // Один экземпляр на всё приложение

// Регистрация с фабричным методом
builder.Services.AddScoped<IEmailService>(provider =>
{
    var config = provider.GetRequiredService<IConfiguration>();
    var logger = provider.GetRequiredService<ILogger<SmtpEmailService>>();
    
    return new SmtpEmailService(logger)
    {
        SmtpServer = config["EmailSettings:SmtpServer"]
    };
});

// Регистрация нескольких реализаций
builder.Services.AddScoped<IEmailService, SmtpEmailService>();
builder.Services.AddScoped<IEmailService, SendGridEmailService>(); // Альтернативная реализация

6. Внедрение зависимостей в минимальные API

app.MapPost("/api/users/minimal", async (
    [FromBody] User user,
    [FromServices] UserService userService, // Явное указание зависимости
    ILogger<Program> logger) =>            // Автоматическое внедрение
{
    logger.LogInformation("Minimal API: создание пользователя");
    await userService.RegisterUserAsync(user);
    return Results.Ok(new { Message = "Пользователь создан через Minimal API" });
});

// С использованием групп
var userGroup = app.MapGroup("/api/users")
    .WithTags("Users")
    .WithOpenApi();

userGroup.MapPost("/", async (User user, UserService service) =>
{
    await service.RegisterUserAsync(user);
    return Results.Created($"/api/users/{user.Id}", user);
});

Ключевые преимущества DI в ASP.NET Core:

  • Слабосвязность: Компоненты зависят от абстракций, а не от конкретных реализаций
  • Тестируемость: Легко подменять реализации моками при юнит-тестировании
  • Управление временем жизни: Гибкая настройка времени жизни объектов
  • Упрощение конфигурации: Централизованная регистрация всех зависимостей
  • Автоматическое разрешение: Контейнер сам создает и внедряет зависимости

Пример юнит-теста с использованием DI:

[Test]
public async Task RegisterUser_SendsWelcomeEmail()
{
    // Arrange
    var mockEmailService = new Mock<IEmailService>();
    var mockRepository = new Mock<IUserRepository>();
    var mockLogger = new Mock<ILogger<UserService>>();
    
    var userService = new UserService(
        mockRepository.Object,
        mockEmailService.Object,
        mockLogger.Object);
    
    var user = new User { Id = 1, Email = "test@example.com", Name = "Test" };
    
    // Act
    await userService.RegisterUserAsync(user);
    
    // Assert
    mockEmailService.Verify(
        es => es.SendEmailAsync(
            "test@example.com",
            "Добро пожаловать!",
            It.IsAny<string>()),
        Times.Once);
}

Данный пример демонстрирует полный цикл использования DI в ASP.NET Core: от определения интерфейсов до их регистрации и внедрения в контроллеры и сервисы. Такой подход делает код более модульным, тестируемым и легким для поддержки.