Ответ 1
В этом случае я бы строго отличался между EF и NH, и я бы не включил обе технологии в один и тот же вопрос. Простой NH более зрелый и имеет архитектуру, ведущую к коду, который можно более легко протестировать. Кроме того, в случае NH вы можете просто переключить базу данных на другую (например, SQLite), и она будет работать так же, что не обязательно должно быть истинным в случае EF, когда коммутационная база данных может привести к тестированию совершенно другого приложения, особенно если вы переключаетесь между MS и базой данных, отличной от MS.
Что такое репозиторий? Давайте посмотрим Определение Мартина Фаулера:
Репозиторий выступает посредником между слоями отображения домена и данных, действуя подобно коллекции объектов домена в памяти. Объекты клиента конструировать спецификации запросов декларативно и представить их Репозиторий для удовлетворения. Объекты могут быть добавлены и удалены из Репозиторий, как они могут из простой коллекции объектов, и код преобразования, инкапсулированный Репозиторием, будет выполнять соответствующие операции за кулисами. Концептуально, репозиторий инкапсулирует набор объектов, хранящихся в хранилище данных, и выполняемые над ними операции, обеспечивая более объектно-ориентированное представление слоя устойчивости. Репозиторий также поддерживает цель достижение чистого разделения и односторонней зависимости между доменом и слоев отображения данных.
Хорошее определение. Теперь подумайте о цели DbSet
:
- Он действует как в коллекции памяти? Да, вы можете использовать его для получения сущностей из базы данных или использования свойства
Local
для получения уже загруженных объектов. - Можно ли декларативно запросить спецификации клиента? Да, это называется linq-to-entity.
- Можно ли добавлять или удалять объекты из коллекции? Да, возможно.
- Является ли отображение инкапсулированным? Да, это так.
- Есть ли чистое разделение? С точки зрения логики да. С точки зрения API нет, потому что экспозиция
IDbSet
для модели домена сделает модель домена зависимой от технологии - EF. Это проблема? Теоретически да, для пуриста да, но в 99% это действительно не проблема, потому что ситуация, когда вам нужно изменить API, редка и всегда связана с большими изменениями, даже если вы правильно отделили API.
DbSet
- это репозиторий. Единственное различие между непосредственным использованием DbSet
и его переносом в какой-то общий репозиторий - это разделение. Это приводит к моему прежнему ответу на аналогичный вопрос - Общий репозиторий с EF 4.1, в чем смысл
Теперь, какова цель репозитория в вашем приложении? Я видел ваши предыдущие вопросы, включая этот, где у вас есть BaseRepository
, построенный поверх фреймворка Entity. Если вы серьезно относитесь к этому как к базовому репозиторию, который будет родительским для ваших специализированных репозиториев, работающих на совокупных корнях для вашей модели домена, и выставляя специализированные методы, относящиеся только к определенному открытому типу объекта, тогда да - вы используете шаблон репозитория, и вам это нужно. Но если вы просто обертываете контекст и единый набор и вызываете этот репозиторий, скорее всего, вы создаете только избыточный слой с сомнительным добавленным значением, потому что это всего лишь оболочка поверх DbSet
.
Существует только один сценарий, в котором ваш репозиторий (обертка DbSet
) будет иметь смысл в этом случае:
- Обертка никогда не выведет
IQueryable
(linq-to-entity) - Обертка никогда не примет
Expression<>
и передаст ее внутриIQueryalbe
(linq-to-entity)
Это единственный сценарий, который предложит вам полностью mockingable repositories = > ваш верхний уровень можно легко протестировать. Вы не собираетесь в репозитории unit test, и вы не собираетесь имитировать контекст, используемый в репозиториях. Репозитории обертывают доступ к данным и логику отображения - единственными разумными тестами в случае репозиториев являются интеграционные тесты. В чем проблема этого сценария? Вы потеряете всю силу LINQ, и вам придется обернуть/повторить реализацию некоторых методов и типов, реализованных в EF. Такие хранилища такие же, как и при обертке доступа к данным с помощью хранимых процедур.
Если вы не следуете этому сценарию, ваш концерт будет намного проще. У вас будут запросы, определенные LINQ, но вы не сможете использовать unit test код, потому что не существует mock/fake, который все равно будет оценивать запросы как linq-to-entity. Как только вы издеваетесь DbSet
или IQueryable
, вы будете использовать linq-to-object, который является расширением linq-to-entity. Вы можете легко написать запрос, который пройдет тест на mocked DbSet
, но сбой во время выполнения с реальным DbSet
. Здесь больше об этой проблеме, и здесь является примером запроса, который пройдет тест, но не работает во время выполнения, В этом случае вы должны использовать тесты интеграции (с реальной базой данных) для всех методов, используя запросы linq-to-entity поверх ваших репозиториев.