Общий репозиторий или Специальный репозиторий для каждой сущности?
В компании, в которой я работаю, они приказали мне обновить старое приложение MVC и внедрить шаблон репозитория для базы данных SQL. Я создал контекст базы данных, используя Entity Framework Database-First, и получил 23 объекта.
Мои вопросы:
Нужно ли создавать репозиторий для каждой сущности ИЛИ реализовывать общий репозиторий для контекста. Я спрашиваю об этом, потому что я нашел следующее при поиске в Интернете:
Второй вопрос: работает ли универсальный репозиторий для Entity Framework Database-First? Это потому, что я нашел следующее при поиске в интернете:
Благодарю.
1 ответ
Для начала, если вы используете полную ORM, такую как Entity Framework или NHibernate, вам следует избегать реализации дополнительного уровня Repository и Unit Of Work. Это потому что; сама ORM выставляет и общий репозиторий, и единицу работы.
В случае EF, ваш DbContext
является единицей работы и DbSet
является универсальным репозиторием В случае NHibernate это ISession
сам.
Создание новой оболочки Общего хранилища поверх того же существующего - это повторная работа. Зачем изобретать велосипед?
Но некоторые утверждают, что использование ORM непосредственно в вызывающем коде имеет следующие проблемы:
- Это делает код немного сложнее.
- Код доступа к данным объединен в бизнес-логике.
- Поскольку многие объекты ORM используются для вызова кода в строке, очень сложно выполнить модульное тестирование кода.
- Поскольку ORM предоставляет только общий репозиторий, это вызывает много проблем, упомянутых ниже.
Помимо всего вышесказанного, еще один вопрос, который обычно обсуждается, это "Что если мы решим изменить ORM в будущем". Это не должно быть ключевым моментом при принятии решения, потому что:
- Вы редко меняете ORM, в основном НИКОГДА - ЯГНИ.
- Если вы меняете ORM, вам все равно придется делать огромные изменения. Вы можете минимизировать усилия путем инкапсуляции полного кода доступа к данным (НЕ просто ORM) внутри чего-либо. Мы обсудим это что- то ниже.
Принимая во внимание четыре проблемы, упомянутые выше, может возникнуть необходимость в создании репозиториев, даже если вы используете полную ORM - это решение для каждого конкретного случая.
Даже в этом случае универсальный репозиторий следует избегать. Это считается анти-паттерном.
Почему общий репозиторий является анти-паттерном?
- Хранилище является частью моделируемого домена, и этот домен не является общим.
- Не каждая сущность может быть удалена.
- Не каждая сущность может быть добавлена
- Не у каждого объекта есть хранилище.
- Запросы сильно различаются; API хранилища становится таким же уникальным, как и сам объект.
- За
GetById()
Типы идентификаторов могут быть разными. - Обновление определенных полей (DML) невозможно.
- Общий механизм запросов является обязанностью ORM.
- Большинство ORM предоставляют реализацию, которая очень похожа на Generic Repository.
- Репозитории должны реализовывать СПЕЦИАЛЬНЫЕ запросы для сущностей, используя общий механизм запросов, предоставляемый ORM.
- Работа с составными ключами невозможна.
- В любом случае, в Сервисах происходит утечка логики DAL.
- Критерии предиката, если вы принимаете их в качестве параметра, должны быть предоставлены из сервисного уровня. Если это специфичный для ORM класс, он пропускает ORM в Сервисы.
Я предлагаю вам прочитать эти ( 1, 2, 3, 4, 5) статьи, объясняющие, почему универсальный репозиторий является анти-паттерном. Этот другой ответ обсуждает шаблон хранилища в целом.
Итак, я предложу:
- НЕ используйте репозиторий, используйте ORM непосредственно в своем коде вызова.
- Если вам нужно использовать репозиторий, не пытайтесь реализовать все с помощью универсального репозитория.
Вместо этого, необязательно, создайте очень простой и небольшой универсальный репозиторий в качестве абстрактного базового класса. ИЛИ вы можете использовать Generic Repository, предоставляемый вашим ORM, в качестве базового хранилища, если ORM позволяет это.
Реализуйте конкретные хранилища в соответствии с вашими потребностями и извлеките все их из общего хранилища. Выставить конкретные репозитории для вызывающего кода.
Таким образом, вы получаете все преимущества универсального репозитория, по-прежнему обходя его недостатки.
Несмотря на то, что это очень редко, это также помогает переключать ORM в будущем, так как код ORM четко абстрагируется в DAL/Repositories. Пожалуйста, поймите, что переключение ORM не является основной целью уровня доступа к данным или репозитория.
В любом случае, не подвергайте Generic Repository вызывающему коду.
Кроме того, не вернуть IQueryable
из конкретных хранилищ. Это нарушает основную цель существования репозитория - абстрагировать доступ к данным. С разоблачением IQueryable
вне хранилища многие решения о доступе к данным просачиваются в вызывающий код, и хранилище теряет контроль над ним.
мне нужно создать репозиторий для каждой сущности или реализовать общий репозиторий для контекста
Как предложено выше, создание хранилища для каждой сущности - лучший подход. Обратите внимание, что в идеале репозиторий должен возвращать модель домена вместо сущности. Но это другая тема для обсуждения.
работает ли общий репозиторий для EF Database First?
Как было предложено выше, EF сама предоставляет общий репозиторий. Строить еще один слой на нем бесполезно. Ваше изображение говорит то же самое.