Как можно внедрить зависимости в контроллер ASP.NET Core?

«Как можно внедрить зависимости в контроллер ASP.NET Core?» — вопрос из категории ASP.NET Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основной и рекомендуемый способ — внедрение через конструктор (Constructor Injection). Контейнер зависимостей ASP.NET Core автоматически разрешает и предоставляет все зависимости, объявленные в конструкторе контроллера.

Пример:

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    private readonly IOrderRepository _orderRepository;
    private readonly ILogger<OrdersController> _logger;
    // Контейнер DI внедрит сюда реализацию IOrderRepository и ILogger
    public OrdersController(IOrderRepository orderRepository, ILogger<OrdersController> logger)
    {
        _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
        _logger = logger;
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Order>> GetOrder(int id)
    {
        _logger.LogInformation("Fetching order {OrderId}", id);
        var order = await _orderRepository.GetByIdAsync(id);
        if (order == null) return NotFound();
        return order;
    }
}

Что нужно сделать для работы:

  1. Зарегистрировать сервисы в контейнере (Program.cs или Startup.ConfigureServices).
    builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();
    // ILogger регистрируется инфраструктурой автоматически

Альтернативные способы (используйте реже):

  • Внедрение в метод действия через атрибут [FromServices]:
    public IActionResult Get([FromServices] IEmailService emailService) { ... }

    Плюс: Удобно для одноразового использования. Минус: Ухудшает тестируемость и скрывает зависимости класса.

  • Получение сервиса из HttpContext.RequestServices (Service Locator - антипаттерн):
    var service = HttpContext.RequestServices.GetService<IMyService>();

    Избегайте этого, так как это усложняет понимание зависимостей и тестирование.

Вывод: Constructor Injection — стандарт. Он делает зависимости явными, упрощает модульное тестирование (зависимости легко подменить mock-объектами) и следует принципу явных зависимостей.