Как избежать избыточного кода DI в ASP.NET Core

автор vadim


При работе с контроллерами в веб-приложениях, использующих ASP.NET Core или ASP.NET Core MVC, вы могли столкнуться с избыточностью кода. Например, вы могли встретить контроллеры, которые используют внедрение зависимостей (DI) для внедрения необходимых им сервисов. Если код, внедряющий зависимости, повторно использовался в нескольких контроллерах, то у вас избыточность кода и нарушение принципа DRY (не повторяйтесь).

В этой статье рассматривается избыточность кода DI и показано, как создать собственный базовый контроллер, чтобы избежать подобных проблем. Для работы с примерами кода, представленными в этой статье, в вашей системе должна быть установлена ​​Visual Studio 2019. Если у вас еще нет копии, вы можете скачать Visual Studio 2019 здесь.

Создайте проект ASP.NET Core MVC в Visual Studio.

Прежде всего давайте создадим проект ASP.NET Core в Visual Studio 2019. Выполнение этих шагов позволит создать новый проект ASP.NET Core MVC в Visual Studio 2019.

  1. Запустите интегрированную среду разработки Visual Studio.
  2. Нажмите «Создать новый проект».
  3. В окне «Создать новый проект» выберите «ASP.NET Core Web App (Model-View-Controller)» из списка отображаемых шаблонов.
  4. Нажмите “Далее.
  5. В окне «Настроить новый проект» укажите имя и местоположение нового проекта.
  6. При желании установите флажок «Поместить решение и проект в один каталог», в зависимости от ваших предпочтений.
  7. Нажмите “Далее.
  8. В следующем окне «Дополнительная информация» выберите .NET 5.0 в качестве целевой платформы из раскрывающегося списка вверху. В поле «Тип аутентификации» оставьте значение «Нет» (по умолчанию).
  9. Убедитесь, что флажки «Включить Docker», «Настроить для HTTPS» и «Включить компиляцию среды выполнения Razor» сняты, поскольку мы не будем здесь использовать ни одну из этих функций.
  10. Нажмите Создать.

Будет создан новый проект ASP.NET Core MVC. Мы будем использовать этот проект для работы с внедрением зависимостей в последующих разделах этой статьи.

Теперь выполните шаги, описанные ниже, чтобы создать дополнительные контроллеры в вашем проекте.

  1. Щелкните правой кнопкой мыши папку решения «Контроллеры».
  2. Выберите Добавить -> Контроллер.
  3. В диалоговом окне «Добавить новый шаблонный элемент» выберите API в качестве шаблона (по умолчанию будет выбран MVC).
  4. Выберите пункт «API-контроллер с действиями чтения/записи».
  5. Нажмите Добавить.
  6. В диалоговом окне «Добавить новый элемент», показанном ниже, укажите имя для вашего нового контроллера.
  7. Нажмите Добавить.

Встроенные классы базового контроллера в ASP.NET Core.

Существует два базовых класса контроллеров: ControllerBase и Controller. Класс ControllerBase реализует интерфейс IController и обеспечивает реализацию нескольких методов и свойств. Он определяет абстрактный метод с именем ExecuteCore, который используется для поиска метода действия и его выполнения. Вам следует использовать ControllerBase всякий раз, когда вы создаете свои API.

Класс Controller расширяет класс ControllerBase, предоставляет метод ExecuteCore и добавляет несколько методов, которые вы можете использовать в своих классах контроллеров, таких как View() и Redirect(). Как и ControllerBase, класс Controller является базовым классом контроллера с поддержкой представлений. Следовательно, вам следует использовать класс Controller всякий раз, когда вы создаете контроллеры в ASP.NET Core MVC. Класс ControllerBase обеспечивает необходимую интеграцию с маршрутизацией и HttpContext, чтобы вы могли их использовать. Он также содержит код, необходимый для управления ViewData и TempData.

Реализуйте класс базового контроллера в ASP.NET Core.

Когда мы создаем новый класс контроллера API в ASP.NET Core, он по умолчанию расширяет класс ControllerBase. Далее мы создадим реализацию базового контроллера. Наш базовый класс контроллера расширит класс ControllerBase платформы.

Вот класс сущности, который мы будем использовать в этом примере:

public class Order
    {
        public int Id { get; set; }
        public int CustomerId { get; set; }
        public string Address { get; set; }       
    }

Теперь создайте следующий интерфейс под названием IOrderManager, который содержит объявление метода с именем ProcessOrder.

public interface IOrderManager
    {
        public void ProcessOrder(Order order);
    }

Затем создайте класс OrderManager, который расширяет интерфейс IOrderManager и реализует его метод ProcessOrder.

public class OrderManager : IOrderManager
{
   public void ProcessOrder(Order order)
   {
      throw new System.NotImplementedException();
   }
}

В следующем листинге кода показано, как можно создать базовый класс контроллера, унаследовав его от класса ControllerBase.

[Route("api/[controller]")]
[ApiController]
public class BaseController : ControllerBase
{
   protected readonly ILogger<BaseController> _logger;
   protected readonly IOrderManager _orderManager;
   public BaseController(ILogger<BaseController> logger,
   IOrderManager orderManager)
   {
       _logger = logger;
       _orderManager = orderManager;
   }
}

Расширьте свой собственный базовый контроллер в ASP.NET Core

Теперь вы можете создавать свои контроллеры на основе только что созданного пользовательского базового контроллера. Следующий листинг кода показывает, как можно создать класс контроллера, расширив его из вновь созданного базового класса контроллера.

[Route("api/[controller]")]
[ApiController]
public class OrderController : BaseController
{
   private readonly ILogger<OrderController> _logger;
  [HttpGet]
   public string Get()
   {
       return "OrderController";
   }
   [HttpPost]
   public void ProcessOrder(Order order)
   {
      _orderManager.ProcessOrder(order);
   }
}

Однако вы обнаружите, что приведенный выше код не скомпилируется. Вот ошибка, с которой вы столкнетесь в Visual Studio:

ошибка класса базового контроллера ИДГ

В ошибке говорится, что вам необходимо реализовать другой конструктор аргументов с теми же аргументами, что и конструктор класса BaseController. Но почему? Если вы собираетесь реплицировать код внедрения зависимостей во все ваши контроллеры, которые расширяют созданный вами базовый класс контроллера, это противоречит цели расширения класса.

Чтобы решить эту проблему, вы можете воспользоваться методом расширения HttpContext.RequestServices.GetService. Помните, что экземпляр HttpContext будет иметь значение null, если вы попытаетесь получить к нему доступ внутри конструктора ваших контроллеров.

Службы, доступные как часть запроса ASP.NET Core, доступны через коллекцию HttpContext.RequestServices. Это означает, что когда вы запрашиваете услугу, запрос будет обработан из этой коллекции.

Добавьте HttpContext в класс базового контроллера в ASP.NET Core.

Вот обновленный исходный код нашего класса BaseController.

public abstract class BaseController<T> : ControllerBase where T : BaseController<T>
{
  private ILogger<T> _logger;
  protected ILogger<T> Logger => _logger ?? (_logger = HttpContext.RequestServices.GetService<ILogger<T>>());       
}

Класс OrderController расширяет этот абстрактный класс BaseController, как показано в листинге кода, приведенном ниже.

[Route("api/[controller]")]
[ApiController]
public class OrderController : BaseController<OrderController>
    {
        private readonly IOrderManager _orderManager;
        public OrderController(IOrderManager orderManager)
        {
            _orderManager = orderManager;
        }
        [HttpGet]
        public string Get()
        {
            Logger.LogInformation("Hello World!");
            return "Inside the Get method of OrderController";
        }
        [HttpPost]
        public void ProcessOrder(Order order)
        {
            _orderManager.ProcessOrder(order);
        }
    }

Вот и все! OrderController может использовать экземпляр Logger, а также внедрение конструктора для внедрения других сервисов.

Наконец, не забудьте зарегистрировать свои службы в методе ConfigurationServices вашего класса Startup, как показано ниже.

public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IOrderManager,OrderManager>();
            services.AddControllersWithViews();
        }

Лучше всего разрешать зависимости с помощью параметризованного конструктора, т. е. с помощью внедрения конструктора. Это поможет вам создавать классы, которые легче тестировать и поддерживать.

Дальше читайте это:

  • Облачные вычисления больше не являются пустяком
  • Что такое генеративный ИИ? Искусственный интеллект, который создает
  • Программирование с помощью ИИ: советы и лучшие практики от разработчиков
  • Python пытается удалить GIL и повысить параллелизм
  • 7 причин, по которым Java по-прежнему хороша
  • Война за лицензирование открытого исходного кода окончена

Related Posts

Оставить комментарий