Универсальный репозиторий с EF 4.1 в чем смысл

145

По мере того, как я углубляюсь в DbContext, DbSet и связанные с ними интерфейсы, я удивляюсь, почему вам нужно реализовать отдельный «универсальный» репозиторий вокруг этих реализаций?

Похоже, что DbContext и IDbSet делают все, что вам нужно, и включают «Единицу работы» внутри DbContext.

Я что-то здесь упускаю или кажется, что людям нравится добавлять еще один уровень зависимости без причины.

Код Jammr
источник
Это немного спорный / основанный на мнении вопрос. Я обсуждал это здесь .
Амит Джоши

Ответы:

202

Вы на самом деле правы. DbContextявляется реализацией шаблона единицы работы и IDbSetявляется реализацией шаблона репозитория.

Хранилища в настоящее время очень популярны и чрезмерно используются. Все используют их только потому, что существуют десятки статей о создании репозитория для структуры сущностей, но на самом деле никто не описывает проблемы, связанные с этим решением.

Основными причинами использования репозитория обычно являются:

  • Скрыть EF из верхнего слоя
  • Сделайте код лучше тестируемым

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

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

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

Я слежу за блогом Айенде . Если вы когда-либо использовали NHibernate, вы, вероятно, знаете его статьи. Этот парень недавно написал несколько статей против использования репозитория с NHibernate, но NHibernate гораздо лучше подшучивает.

Ладислав Мрнка
источник
3
Вы можете высмеивать, IDbSetвы также можете определить пользовательский интерфейс в производном контексте, но это все. Как только ваш код использует ChangeTracker, Entries или что-то еще, потребуется много усилий, чтобы обернуть их все.
Ладислав Мрнка
1
Да, EF не очень ориентирован на производительность. По крайней мере, у MS есть много возможностей сделать это лучше в будущих версиях.
Ладислав Мрнка
2
@ chiccodoro: верно. Но как только ваш смоделированный класс предоставляет IQueryableили принимает в Expression<>качестве параметра, который внутренне помещается в запрос Linq-to-entity, вы определяете логику вне смоделированного компонента с побочными эффектами, которые невозможно проверить с помощью модульных тестов.
Ладислав Мрнка
8
Если я использую DbSet и BdContext прямо там, на своем бизнес-уровне, я должен ссылаться на EntityFramework.dll там же, как и в моем проекте DataLayer. Уже одно это говорит мне о том, что для этого нужна какая-то упаковка.
Ingó Vals
2
downvote: не завершено - абстрагирование EF за интерфейсом хранилища может привести к тому, что один и тот же клиентский код будет запущен как в SL, так и в WPF.
h.alex
21

Я борюсь с теми же проблемами, и важна насмешливость для модульного тестирования слоев EF. Но я наткнулся на эту замечательную статью, в которой объясняется, как настроить EF 4.1 DbContext для подделки, убедившись, что ваш производный DbContext реализовал общий интерфейс и предоставляет IDbSet, а не DbSet. Поскольку я использую подход Database First, поскольку наша база данных уже существует, я просто изменил шаблоны T4, использованные для генерации моего производного DbContext, чтобы он генерировал интерфейсы IDbSet, а также производные от моего общего интерфейса. Таким образом, все это может быть легко смоделировано, и вам не нужно реализовывать собственную единицу работы или шаблон репозитория. Просто напишите свой сервисный код, чтобы использовать ваш общий интерфейс, и когда вы перейдете к его тестированию,

http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/

Кендалл Беннетт
источник
5

Одна из причин создания репозитория заключается в том, что вы можете скрыть реализацию DBSet и DbContext, если решите перейти от EntityFramework к чему-то другому или наоборот.

Например, я использовал NHibernate, и я обернул все вызовы этой платформы в свои классы репозитория. Они возвращают IEnumerable для того, чтобы они были «общими», а мои репозитории имеют стандартные операции CRUD (обновление, удаление и т. Д.). Я давно перешел на Entity Framework. После этого мне не нужно было ничего менять в моих классах ViewModel или за их пределами, потому что они указывали на мой репозиторий - мне нужно было только изменить внутреннюю часть своего репозитория. Это значительно облегчило жизнь при миграции.

(Я использовал NHibernate, потому что мы подключаемся к ISeries, и в то время не было экономически эффективных реализаций, использующих EF с ISeries. Единственной доступной была оплата IBM $ 12 000 за их DB2Connect)

Лениэль Маккаферри
источник
«Почти» (в отношении скрытия DBSet и DbContext) вы обнаружите, что вам не нужно предоставлять EF каким-либо потребителям (например, если вы используете DI), но вам нужен интерфейс, который предоставляет свойства IDbSet <T> или сделайте еще один шаг и вместо этого введите все свои свойства как IQueryable <T>, но я хочу сказать, что вы можете полностью скрыть свою зависимость от DbSet и DbContext. Операции CRUD затем могут быть записаны как методы расширения, вы можете написать несколько методов расширения для разных хранилищ. Однако вы не будете скрывать использование LINQ.
Шон Уилсон