Когда использовать шаблон хранилища


20

Недавно я прочитал, что не рекомендуется использовать шаблон хранилища в сочетании с ORM. Насколько я понимаю, это потому, что абстракция, которую они предоставляют для базы данных SQL, слишком неплотна, чтобы сдерживать шаблон.

У меня есть пара вопросов по этому поводу:

  1. Что вы делаете, если хотите отключить ORM? У вас будет специальный код ORM в вашем приложении, если вы не содержите его в репозитории.

  2. Является ли шаблон хранилища действительным, если не используется ORM, и вы используете ADO.NET для доступа к данным и самостоятельно заполняете данные объекта?

  3. Если вы используете ORM, но не шаблон репозитория, где вы храните часто используемые запросы? Было бы разумно представлять каждый запрос как класс и иметь какую-то фабрику запросов для создания экземпляров?


1
1) вы никогда не сможете поменять ORM из-за их разного поведения, даже если оба поддерживают linq, они будут вести себя достаточно по-разному, чтобы ваше приложение могло сломаться. например, ленивое поведение при загрузке, грязное отслеживание, прокси-серверы-призраки и т. д. Однако приятно иметь возможность заменять ORM для реализации in-mem для тестирования.
Роджер Йоханссон,

Что бы это ни стоило, мой взгляд на обсуждение репозитория / ORM здесь: stackoverflow.com/questions/13180501/…
Эрик Кинг,

Где вы читали, что это плохая практика?
Дэйв Хиллиер,

Ответы:


3

1) Верно, но как часто вы выключаете ORM?
2) Я бы сказал так, потому что контекст ORM является своего рода хранилищем и скрывает много работы, включая создание запросов, получение данных, отображение и т. Д. Если вы не используете ORM, эта логика все еще должна где-то находиться. Я не знаю, будет ли это квалифицироваться как шаблон репозитория в самом строгом смысле этого слова ...
3) Я часто вижу инкапсуляцию запросов, но обычно это больше для целей тестирования / заглушки. Кроме этого, я был бы осторожен при повторном использовании запроса в разных частях приложения, потому что тогда вы рискуете создать зависимость от чего-то, что может измениться n раз (n - количество мест, где вы используете запрос).


1
1) Вы задаете не тот вопрос. Неважно, как часто, это просто должно быть один раз. Учитывая количество доступных опций ORM, может оказаться, что он лучше, чем тот, который вы уже используете. Теперь возникает вопрос: что происходит, когда вы делаете? Хранилище дает вам хорошую абстракцию. Я был там, и я хотел бы сделать такую ​​абстракцию в первую очередь.
devnull

3
@devnull Я не согласен. Если это случится максимум один раз, я бы посчитал это приемлемым риском. Если вы боитесь сделать неправильный выбор: поэкспериментируйте больше, прежде чем совершать один. Теоретически, такая абстракция звучит хорошо, но на практике вы воссоздаете довольно большую часть API вашего ORM, и все потому, что вы можете выбрать другой когда-нибудь, где-нибудь. Для меня это пустая трата усилий, избыточный код и больше кода, который вы сами должны поддерживать и гарантировать. Кроме того, переключение ORM не должно влиять на все приложение; научиться расставлять доменные границы.
Стефан Биллиет

Изменение ORM - не единственная причина для хранилища. Если вам необходимо внедрить [распределенный] кеш в ваше приложение - все изменения будут сделаны в хранилище, и ваш BL не изменится из-за изменений уровня доступа к данным.
Валерий

не будет ли распределенный кеш встроен в ORM в большинстве случаев?
user1450877

Я думаю, что это зависит от ORM. Вы можете выбрать более легкий ORM, чем NHibernate или EF.
Валерий

2

1) Что вы делаете, если вы хотите отключить ORM, у вас будет конкретный код ORM в вашем приложении, если вы не будете содержать его в репозитории.

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

Тогда у меня была бы другая сборка для конкретной реализации моего уровня доступа к данным. Например, я мог бы иметь:

Company.Product.Dataи Company.Product.Data.EntityFrameworkсборки. Первая сборка будет использоваться исключительно для интерфейсов, тогда как другая будет конкретной реализацией логики доступа к данным Entity Framework.

2) Является ли шаблон хранилища действительным, если не используется ORM, и вы используете ADO.net для доступа к данным и заполнения данных объекта самостоятельно?

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

Я видел класс репозитория, который начинался с обязанностей GetAll, GetById, Update и Delete, что хорошо. К тому времени, когда проект был завершен, у того же класса были десятки методов (обязанностей), которых никогда не должно было быть. Например, GetByForename, GetBySurname, UpdateWithExclusion и всякие сумасшедшие вещи.

Это где запросы и команды вступают в игру.

3) Если вы используете ORM, но не шаблон репозитория, где вы храните часто используемые запросы. Было бы разумно представлять каждый запрос как класс и иметь какую-то фабрику запросов для создания экземпляров?

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

  • Определить интерфейс для запроса. В этом вам поможет юнит тест. Напримерpublic interface IGetProductsByCategoryQuery { ... }

  • Определите конкретную реализацию для запроса. Вы сможете внедрить их через IoC-фреймворк по вашему выбору. Напримерpublic class GetProductsByCategoryQuery : IGetProductsByCategoryQuery

Теперь вместо того, чтобы загрязнять хранилище десятками обязанностей, я просто группирую свои запросы в пространства имен. Например, интерфейс для вышеупомянутого запроса может жить в: Company.SolutionName.Products.Queriesи реализация может жить вCompany.SolutionName.Products.Queries.Implementation

Когда дело доходит до обновления или удаления данных, я использую шаблон команд таким же образом.

Некоторые могут не согласиться и сказать, что до завершения проекта у вас будут десятки классов и пространств имен. Да, вы будете. На мой взгляд, это хорошая вещь, так как вы можете просмотреть решение в IDE по вашему выбору и сразу увидеть, какие обязанности выполняет определенный компонент. Если вы решили вместо этого использовать шаблон репозитория, вам придется заглянуть внутрь каждого класса репозитория, пытаясь выяснить его обязанности.


Мне нравится идея иметь команды вместо общих функций. Где я могу прочитать больше о том, как реализовать их в контексте доступа к данным?
ankush981

1

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: то, что следует, основано на моем понимании и кратком опыте упомянутой модели (репозитория). Я мог бы делать это неправильно ... на самом деле, я довольно уверен, что я делаю это неправильно :). Итак, хотя это попытка ответа, это также скрытый вопрос.

Я использую шаблон Repository для абстрагирования от слоя доступа к данным, который в большинстве случаев является ORM. Это универсальный интерфейс с методами для классов Create, Read, Update, Delete и реализации для LINQ to SQL и EF. Я мог бы сделать реализацию, которая записывает в XML-файлы на диске (просто дикий пример того, что я мог с этим сделать). Я не внедряю Unit of Work, поскольку он поддерживается ORM. В случае необходимости, я мог бы реализовать это. Мне так нравится, потому что пока это дало мне хорошее разделение. Я не говорю, что нет лучших альтернатив.

Чтобы ответить на ваши вопросы:

  1. Нравится вам это или нет, изменения произойдут. И если вы написали кучу приложений, и пришло время их поддерживать, но вы чувствуете, что работать с текущим ORM очень сложно и хотите изменить его, вы похвалите себя за создание такой абстракции. Просто используйте все, что вам удобно, будь то репозиторий или другой шаблон / концепция.
  2. Да, если вы используете его для разделения доступа к данным. Как я упоминал ранее, вы можете сделать реализацию, которая записывает данные в виде плоских файлов.
  3. Я использую репозиторий, чтобы сохранить свои обычно используемые запросы и объекты запросов, когда у меня есть запросы, которые я хочу настроить (которых не так много).

В качестве другого примера, ребята из Umbraco абстрагировали контейнер DI, на тот случай, если они захотят переключиться на что-то другое.


0

Если ORM предлагает гибкость LINQ, нетерпеливую загрузку и т. Д., Я бы не стал скрывать это за дополнительным слоем.
Если он включает в себя необработанный sql (микро ORM), то в любом случае следует использовать «метод на запрос» для достижения возможности повторного использования, что делает шаблон репозитория подходящим.

What do you do if you want to switch out ORMs? 
You would have ORM specific code in your application if you do not contain it in a repository.

Зачем вам нужно переключаться?
Тот, который имеет большинство функций, которые вам нужны, следует использовать. Возможно, что новая версия ormX принесет новые функции и окажется лучше, чем текущая, но ...
Если вы решите скрыть orm, вы можете использовать только те функции, которые имеют все кандидаты.
Например, вы не можете использовать Dictionary<string, Entity>свойства в своих объектах, потому что ormY не может их обработать.

Предполагая, что используется LINQ, большинство переключателей orm просто переключают ссылки на библиотеки и заменяют их session.Query<Foo>()на context.Foosаналогичные, пока они не скомпилируются. Скучная задача, но занимает меньше времени, чем кодирование уровня абстракции.

Is the repository pattern still valid when not using an ORM and you are using ADO.NET for data access and populating object data yourself?

Да. Код должен быть многоразовым, и это означает, что здание SQL, материализация объекта и т. Д. Должны быть размещены в одном месте (отдельный класс). Вы также можете вызвать класс «XRepository» и извлечь из него интерфейс.

If you use an ORM but not the repository pattern where do you keep commonly used queries? 
Would it be wise to represent each query as a class and have some sort of query factory to create instances?

Предполагая, что LINQ используется, обертка класса будет излишним ИМХО. Более хорошим способом были бы методы расширения

public static IQueryable<T> Published<T>(this IQueryable<T> source) where T : Page
{
    // at some point someone is going to forget to check that status
    // so it makes sense to extract this thing
    return source.Where(x => x.Status == Status.Published && x.PublishTime <= DateTime.UtcNow);
}

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.