Ответ
Основной и рекомендуемый способ — внедрение через конструктор (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;
}
}
Что нужно сделать для работы:
- Зарегистрировать сервисы в контейнере (
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-объектами) и следует принципу явных зависимостей.