Каковы преимущества универсального шаблона репозитория + шаблона UOW с ORM-подобной Entity Framework
Давайте начнем с двух цитат, суммирующих эту проблему: "Заключение DbContext - это утечка абстракции. В любом случае, вы окажетесь в некоторой зависимости от EF на уровне сервисов / контроллеров". цитата ref И вторая цитата:
"Класс DbContext
Представляет комбинацию шаблонов Unit-Of-Work и Repository и позволяет запрашивать базу данных и группировать изменения, которые затем будут записаны обратно в хранилище как единое целое. " MSDN
Пожалуйста, не говорите, что шаблон репозитория позволяет вам просто поменять базу данных, это не так. Кто-нибудь когда-нибудь делал это со зрелым приложением? Пожалуйста, также не отвечайте, что шаблон Repository не должен раскрывать IQueryable, я думаю, это просто еще один способ сказать, что вы не доверяете людям, с которыми работаете.
Я всецело за инкапсуляцию, покрытие кода / тестируемость, но так как этот популярный шаблон репо для EF предоставляет IQueryable, больше нет необходимости в таком шаблоне:
public interface IRepository<T> where T : class
{
IQueryable<T> GetAll();
T GetById(int id);
IEnumerable<T> Get(Expression<Func<T, bool>> filter = null,Func<IQueryable<T>,IOrderedQueryable<T>> orderBy = null, string includeProperties = "");
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(int id);
}
Единственными преимуществами шаблона репозитория в сочетании с шаблоном UOW являются: 1. Простое включение шаблона внедрения зависимостей для повышения тестируемости / охвата кода. 2. Инкапсулируйте сложность, которая существует при внешней интеграции (вызовы веб-служб для различных поставщиков, веб-интерфейс, базы данных)., так далее..)
Так какую инкапсуляцию я получаю, чего не получаю, используя класс MyDbContext: DbContext? Мой ORM - это абстракция. Конечно, я получаю некоторую однородность контроллеров с UOW работы и общими запросами, такими как Add и Delete, но это действительно стоит затраченных усилий. Разве я не могу просто доверять своим разработчикам, чтобы они были едины, и когда они делают это немного по-своему, просто не забывайте об этом. И так как контроллер может написать любой запрос, который он хочет, разве это не бросает вызов модульному тестированию? И (даже большие заглавные буквы), если у меня есть сторонний API для вызова, я могу сделать это доступным в классе MyDbContext для контроллеров, так что это выглядит как очередной вызов данных для контроллера.
Я снова говорю, почему бы не использовать ORM напрямую, это абстракция данных!
Вот несколько аргументов против шаблона репозитория:
http://ayende.com/blog/3955/repository-is-the-new-singleton
http://lostechies.com/jimmybogard/2012/09/20/limiting-your-abstractions/
http://lostechies.com/jimmybogard/2012/10/08/favor-query-objects-over-repositories/
Вот правильный аргумент для шаблона репозитория (хотя и недостаточно убедительный): http://www.sapiensworks.com/blog/post/2012/10/10/Do-We-Need-The-Repository-Pattern.aspx
2 ответа
Я думаю, что сойду с ума, если увижу, что Айенде отправит сообщение еще раз. Давайте рассмотрим каждую вещь медленно:
Универсальный репозиторий имеет ограниченную применимость. Лично я использую его ТОЛЬКО для репозиториев доменов и ТОЛЬКО когда храню агрегатные корни в сериализованной форме. Для всего остального (читай: просмотр моделей, отчетов) у меня был бы репозиторий, разработанный для нужд слоя, запрашивающего эти модели.
IQueryable в значительной степени построитель запросов. Вы не говорите хранилищу, как создать что-то (IQUeryable), вы просто спрашиваете что-то из этого. Если вы используете IQueryable, вы уже выполняете часть работы хранилища (нарушая SRP более высокого уровня). Также предоставление IQueryable означает, что верхний уровень ДОЛЖЕН знать о документах / сущностях, используемых для создания запроса. Если ваш начальник решит, что отныне вы будете получать данные из веб-службы, что бы вы сделали со всеми этими объектами IQueryable и ORM, разбросанными повсюду? Или СУБД слишком медленная, так что давайте использовать Document Db? Да, это может поддержать linq, но вы уверены, что у вас будут определены те же сущности?
Не доверяйте своим разработчикам. Доверьтесь правильной архитектуре, в которой вы не смешиваете обязанности уровня и где соблюдается SRP (принцип единой ответственности). Если вы разрабатываете, скажем, просмотр моделей в соответствии с тем, как данные хранятся в БД, а не с тем, что вам нужно в представлении, у вас есть проблема тесной связи.
Причина, по которой не используется репо, потому что вам не нужно менять ORM или технологию хранения, ИМХО похожа на причину: зачем использовать DI-контейнер или интерфейсы, есть небольшая вероятность, что эти классы изменится, и мы можем сделать вручную инъекция в любом случае.
Использование репозитория означает, что вам не нужно заботиться о том, как и откуда ваши модели извлекаются. Хранилище предоставляет семантику, дружественную бизнесу (GetTopSellingProducts). С помощью orm вы должны создать запрос (все любят выполнять объединения и подзапросы в linq), знать о сущностях, делать прогнозы и т. Д. Почему высший уровень должен заботиться об этих деталях? Это принцип "спрашивай, не говори как" (j/k)
В конце концов, все дело в поддерживаемом коде, я хочу, чтобы мой код был легко понят.
Я снова говорю, почему бы не использовать ORM напрямую, это абстракция данных!
Это абстракция данных, а не абстракция данных.
Речь идет об абстрагировании вашего ORM, если вам нужно его отключить. Не о том, что может предложить общий репозиторий напрямую через DbContext. Если вам нужно было отключить EF и DbContext, и он был сильно связан, вам нужно будет отредактировать все разделы вашего кода, использующие DbContext, или вы будете вынуждены реализовать интерфейс DbContext на любом ORM, на который вы переключаетесь.
Говоря, что я думаю, что вероятность переключения ORM в большинстве приложений невелика, а удобство использования DbContext напрямую перевешивает риск соединения с EF. Тем не менее, я до сих пор понимаю, почему некоторые захотят сделать это в зависимости от требований приложения и срока службы.