Должны ли репозитории предоставлять IQueryable уровню обслуживания или выполнять фильтрацию в реализации? - PullRequest
11 голосов
/ 18 мая 2010

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

public interface IMyRepository
{
  IQueryable<MyClass> GetAll();
}

Реализовано в:

public class LINQtoSQLRepository : IMyRepository
{
   public IQueryable<MyClass> GetAll()
   {
      return from table in dbContext.table
             select new MyClass
             {
                Field1 = table.field1,
                ... etc.
             }
   }
}

Фильтр для идентификаторов:

public static class TableFilters 
{       
   public static MyClass WithID(this IQueryable<MyClass> qry, string id)
   {
      return (from t in qry
              where t.ID == id
              select t).SingleOrDefault();
   }     
}

Вызывается из службы:

public class TableService
{
   public MyClass RecordsByID(string id)
   {
      return _repository.GetAll()
                        .WithID(id);
   }
}

Я столкнулся с проблемой, когда экспериментировал с реализацией репозитория с использованием Entity Framework с LINQ to Entities. Класс filters в моем проекте содержит некоторые более сложные операции, чем «WHERE ... == ...» в приведенном выше примере, который, я считаю, требует разных реализаций в зависимости от поставщика LINQ. В частности, у меня есть требование выполнить предложение SQL "WHERE ... IN ...". Я могу реализовать это в классе фильтра, используя:

string[] aParams = // array of IDs
qry = qry.Where(t => aParams.Contains(t.ID));

Однако, чтобы выполнить это с Entity Framework, мне нужно предоставить решение, такое как BuildContainsExpression , которое связано с Entity Framework. Это означает, что у меня должно быть 2 разных реализации этого конкретного фильтра, в зависимости от основного поставщика.

Буду признателен за любые советы о том, как мне поступить отсюда. Мне казалось, что предоставление IQueryable из моего хранилища позволит мне выполнять фильтрацию на нем независимо от основного поставщика, что позволяет мне переключаться между поставщиками, если и когда это потребуется. Однако проблема, которую я описал выше, заставляет меня думать, что я должен выполнить всю свою фильтрацию в репозиториях и вернуть IEnumerable, IList или отдельные классы.

Большое спасибо, Matt

1 Ответ

14 голосов
/ 02 марта 2011

Это очень популярный вопрос. Тот, который я постоянно спрашиваю себя. Я всегда чувствовал, что лучше всего возвращать IEnumerable, а не IQueryable из репозитория.

Цель хранилища - инкапсулировать инфраструктуру базы данных, чтобы клиенту не приходилось беспокоиться об источнике данных. Однако, если вы вернете IQueryable, вы будете зависеть от потребителя относительно того, какой тип запроса будет выполняться к вашей базе данных, и будут ли они делать что-то, что не поддерживается поставщиком LINQ.

Взять, к примеру, пейджинг. Допустим, у вас есть объект Customer, а в вашей базе данных могут быть сотни тысяч клиентов. Какой код вы бы предпочли написать своему клиенту?

var customers = repos.GetCustomers().Skip(skipCount).Take(pageSize).ToList();

OR

var customers = repos.GetCustomers(pageIndex, pageSize);

При первом подходе хранилище делает невозможным ограничение количества записей, извлекаемых из источника данных. Кроме того, ваш потребитель должен рассчитать skipCount.

Во втором подходе вы предоставляете более грубый интерфейс для вашего клиента. Теперь ваш репозиторий может наложить некоторые ограничения на размер страницы для оптимизации запроса. Вы также инкапсулируете расчет skipCount.

Однако, как говорится, в вашей ситуации ваш клиент - это ваша услуга. Поэтому я полагаю, что вопрос действительно сводится к разделению интересов. Где лучше выполнить такую ​​логику валидации? Ну, этот ответ вполне может быть "на службе". Но как насчет ответа на вопрос «Где лучше содержать логику запроса?». Для меня ответ явно "Репозиторий". Это его предполагаемая область знаний.

...