Последствия использования DBContext с внедрением зависимостей

Я все еще новичок в использовании внедрения зависимостей для управления моим DBContext в приложении ASP.NET MVC, которое я пишу.

Я пытаюсь следовать подходу, описанному в статье Управление жизненным циклом объектов DbContext Lifetime в ASP.NET MVC. По сути, этот подход говорит использовать Ninject и внедрение зависимостей и добавить мой DBContext в качестве параметра в конструктор в моих контроллерах.

Далее, я реализую это в базовом контроллере, или из которого будут получены все мои классы контроллеров.

Это работает, но я затрагиваю следующие вопросы.

  1. Этот подход требует, чтобы каждый производный класс контроллера также реализовывал конструктор, который принимает любые аргументы, требуемые моим базовым классом контроллера. Это не только кажется большим количеством дополнительной типизации, которую я должен помнить, чтобы добавить к любому новому производному классу, но это также означает, что если я изменяю данные, передаваемые в конструктор, то я должен модифицировать конструктор в каждом производном классе контроллера.

  2. Это дает мне DBContext для всех моих классов контроллера. Но как насчет других классов в моей модели, которым нужен DBContext? Должен ли я вручную передать экземпляр в DBContext для всех этих классов? Или есть способ использовать DI для каждого из этих классов, чтобы получить свою собственную копию DBContext?

1 ответ

Решение

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

Это один из подходов (Heavy Controller), который вы можете использовать в своем приложении EF, IMO - не самый чистый подход. И вы правильно заметили недостатки себя.

Если мы связываем этот подход с принципом проектирования, он нарушает принцип единой ответственности, поскольку ожидается, что контроллер будет делать больше (извлекать или обновлять БД), чем просто собирать данные и возвращать соответствующее представление с данными. А как насчет бизнес-правил, будет ли контроллер применять их, если вам нужно отправить электронное письмо, будет ли это делать и контроллер? У вас должен быть еще один уровень бизнес-классов / классов обслуживания, которые специально разработаны для набора требований, например, EmailHelper будет отправлять электронные письма.

Это также нарушает принцип Open Close, так как вам нужно менять конструкторы каждый раз, когда вы меняете входной параметр.

Это дает мне DBContext для всех моих классов контроллера. Но как насчет других классов в моей модели, которым нужен DBContext? Должен ли я вручную передать экземпляр в DBContext для всех этих классов?

Что касается внедрения зависимости, то одной из целей является внедрение зависимости там, где это необходимо напрямую. Если у вас есть класс модели, которому нужен DbContext, вы должны внедрить его в конструктор класса модели (большинство DI-фреймворков также поддерживает внедрение свойств, но конструктор остается любимым подходом).

В DI Framework вы будете настраивать зависимости в одном месте (код инициализации приложения), а затем каждый класс, для которого нужна зависимость, просто принимает его в конструкторе.

Контейнер DI можно сравнить со словарем, в котором ключ - это интерфейс, а значение - готовый объект. После его настройки вы можете в любое время запросить любой объект, используя правильный ключ в вашем приложении.

Или есть способ использовать DI для каждого из этих классов, чтобы получить свою собственную копию DBContext?

Инфраструктура DI поддерживает различные способы создания экземпляров, позволяющие контролировать время существования экземпляра. Как правило, на запрос, на поток и синглтон. Больше информации здесь. Если вы хотите, чтобы каждый контроллер получил копию DbContext, вы можете использовать конфигурацию для каждого запроса при настройке экземпляра DbContext.

Альтернативное решение:

В большинстве моих MVC-приложений у меня был сервисный уровень (набор классов, которые применяют бизнес-правило). Каждый из этих классов был внедрен с DbContext (не точно DbContext, но IDataContext). В контроллеры был введен класс обслуживания, необходимый для извлечения или обновления данных.

Абстрагировав DbContext от IDataContext, я мог бы установить контекст данных заглушки в моем тесте или завтра, если я захочу перейти с EF на NHibernate или что-то более умное DI Framework, мне просто нужно будет реализовать IDataContext и изменить код инициализации зависимости.

Надеюсь это поможет.

Другие вопросы по тегам