Реализация хранилища
Кажется, что каждый пример, который я нахожу в шаблоне репозитория, реализация каким-то образом отличается. Ниже приведены два примера, которые я в основном нахожу.
interface IProductRepository
{
IQueryable<Product> FindAll();
}
Существует, как правило, еще один слой, который говорит с репозиторием и вызывает метод FindAll() и выполняет любые операции, такие как поиск продуктов, начинающихся с буквы или выборки продуктов в определенной категории.
В другом примере, который я нахожу много, ставьте все методы поиска в репозиторий
interface IProductRepository
{
IEnumerable<Product> GetProductsInCategory(int categoryId);
IEnumerable<Product> GetProductsStartingWith(string letter);
IEnumerable<PromoCode> GetProductPromoCodes(int productId);
}
Какой путь вы рекомендуете взять? Или каковы преимущества/недостатки друг от друга?
Из моего понимания, прочитав http://martinfowler.com/eaaCatalog/repository.html, первый подход, похоже, лучше всего отражает это?
Ответы
Ответ 1
Первый ужасен. IQueryable
похож на объект GOD. Очень сложно найти 100% полную реализацию (даже среди всех OR/M). Вы можете разоблачить свой ORM напрямую, а не использовать его, так как вы, вероятно, получите уровень утечки аберрации.
Джоэл говорит, что это лучше всего (текст из статьи wikipedia):
В статье Спольского он обращает внимание на множество примеров абстракций, которые работают большую часть времени, но где нельзя описать детальную сложность, лежащую в основе, и, таким образом, усложняет программное обеспечение, которое должно было быть упрощено за счет абстракции сам
Запись блога Джоэлса
Второй подход намного проще реализовать и сохранить целостность абстракции.
Обновление
Ваш репозиторий нарушает принцип единой ответственности, поскольку он имеет две причины для изменения. Во-первых, если API-интерфейс продуктов изменен, а другой - если API-интерфейс PromoCode изменен. Вы должны imho использовать два разных репозитория, таких как:
interface IProductRepository
{
IEnumerable<Product> FindForCategory(int categoryId);
IEnumerable<Product> FindAllStartingWith(string letter);
}
interface IPromoCodeRepository
{
IEnumerable<PromoCode> FindForProduct(int productId);
}
Измененные вещи:
- Я обычно начинаю методы с
Find
, когда возвращаются несколько элементов и Get
, если возвращается один элемент.
- Более короткие имена методов = легче читать.
- Единая ответственность. Легче сказать, какие классы, которые используют репозитории, имеют зависимости.
Небольшие четко определенные интерфейсы упрощают обнаружение нарушений принципов SOLID, поскольку классы, которые нарушают принципы, имеют тенденцию к раздутым конструкторам.
Ответ 2
Консенсус строит: 2-й вариант полностью. Помимо логики запросов, протекающей повсюду с помощью IQueryable, трудности с ее реализацией правильны, очень сложно тестировать и издеваться.
Ответ 3
Лично я бы предложил использовать второй пример, таким образом, вы инкапсулируете логику поиска в одном месте, и цель вызывающего лица четко определяется именем метода, который они вызывают. Если вы перейдете к первому примеру, ваш код запроса будет протекать по всему вашему приложению, и вы будете дублировать запросы.
Ответ 4
Я рекомендую избегать дублирования. Это первая цель. Если у вас есть логика, которая находит продукты, начиная с некоторой буквы в нескольких местах, то это особый случай и его ценность извлекается для раздельного метода (также он дает хорошее описание для вашего конкретного случая). Код без дублирования намного легче изменить, понять и поддерживать.
Итак, у меня есть один общий метод поиска с IQueryable
и набор методов, которые использовались более одного раза:
interface IRepository<T>
{
IQueryable<T> FindAll();
}
interface IProductRepository : IRepository<Product>
{
IEnumerable<Product> GetProductsInCategory(int categoryId);
IEnumerable<Product> GetProductsStartingWith(string letter);
IEnumerable<PromoCode> GetProductPromoCodes(int productId);
}
Рассмотрим также модульное тестирование. Конкретные методы намного проще высмеивать, чем IQueryable.
Ответ 5
Я действительно думаю, что 1-й лучше. Я принимаю решение о следующих факторах:
-
Если структура продукта получит рефакторинг:
- IQueryable - вам нужно только изменить методы вызова в коде.
- IEnumerables - вам нужно также изменить имена методов.
-
Если многие собираются вывести интерфейсы, которые вы хотите итератировать полиморфным способом:
- IQueryable подход - преимущество универсальных имен унифицированных методов
- IEnumerables - некоторые имена могут не описывать необходимый вам метод.
-
Elasticness
- IQueryable подход - легко разделить на IEnumerables подход.
- Подход IEnumerables - трудно конвертировать обратно в подход IQueryable.
Итак, я предлагаю вам начать с IQueryable в качестве выбора по умолчанию, и по мере продвижения с вашим кодом вы всегда можете перейти к более конкретному подходу IEnumerables, который вам нужен.