Ответ
Основной и рекомендуемый способ — внедрение через конструктор (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-объектами) и следует принципу явных зависимостей.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши зависимости, блядь. Слушай, тут всё просто, как три копейки, но народ умудряется так накосячить, что диву даёшься.
Основной и самый правильный способ — это когда ты внедряешь всё через конструктор (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 — это наш царь и бог. Всё на виду, всё прозрачно, и подсунуть заглушку для тестов — раз плюнуть. Не выёбывайся, используй его.