Должен ли я вернуть IEnumerable <T>или IQueryable <T>из моего DAL? - PullRequest
38 голосов
/ 14 июня 2010

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

Как я понимаю, IQueryable<T> реализует IEnumerable<T>, поэтому в моем DAL в настоящее время у меня есть сигнатуры методов, подобные следующим:

IEnumerable<Product> GetProducts();
IEnumerable<Product> GetProductsByCategory(int cateogoryId);
Product GetProduct(int productId);

Должен ли я использовать IQueryable<T> здесь?

Каковы плюсы и минусы любого подхода?

Обратите внимание, что я планирую использовать шаблон Repository, поэтому у меня будет такой класс:

public class ProductRepository {

    DBDataContext db = new DBDataContext(<!-- connection string -->);

    public IEnumerable<Product> GetProductsNew(int daysOld) {
        return db.GetProducts()
          .Where(p => p.AddedDateTime > DateTime.Now.AddDays(-daysOld ));
    }
}

Должен ли я изменить IEnumerable<T> на IQueryable<T>? Какие преимущества / недостатки есть у одного или другого?

Ответы [ 4 ]

52 голосов
/ 14 июня 2010

Это зависит от того, какое поведение вы хотите.

  • Возвращение IList сообщает вызывающему абоненту, что они получили все запрошенные данные
  • Возвращая IEnumerable сообщает вызывающей стороне, что им нужно будет выполнить итерацию по результату, и он может быть лениво загружен.
  • Возвращение IQueryable сообщает вызывающему, что результат поддерживается провайдером Linq, который может обрабатывать определенные классы запросов, что накладывает бремя на вызывающего, чтобы сформировать запрос с выполнением.

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

7 голосов
/ 17 июня 2010

Еще одна вещь для размышления: где ваша поддержка подкачки / сортировки?Если вы предоставляете поддержку подкачки в своем хранилище, возврат IEnumerable<T> - это нормально.Если вы осуществляете подкачку за пределы своего хранилища (например, на уровне контроллера или службы), то вы действительно хотите использовать IQueryable<T>, потому что не хотите загружать весь набор данных в память до того, как он будет выгружен.

5 голосов
/ 14 июня 2010

огромная разница. Я вижу это совсем немного.

Вы создаете IQueryable до того, как он попадет в базу данных. IQueryable попадает в БД только после вызова активной функции (например, .ToList ()) или когда вы на самом деле пытаетесь извлечь значения. IQueryable = ленивый.

IEnumerable сразу выполнит вашу лямбду против БД. IEnumerable = eager.

Что касается использования с шаблоном Repository, я полагаю, что он готов. Я обычно вижу, как пропускают ILists, но кому-то еще нужно это сгладить для вас. РЕДАКТИРОВАТЬ - вы обычно видите IEnumerable вместо IQueryable, потому что вы не хотите, чтобы слои проходили мимо вашего репозитория.

Есть очень хорошее видео LINQ, которое мне очень нравится - оно поражает больше, чем просто IEnumerable v IQueryable, но оно действительно обладает фантастическим пониманием.

http://channel9.msdn.com/posts/matthijs/LINQ-Tips-Tricks-and-Optimizations-by-Scott-Allen/

3 голосов
/ 15 июня 2010

Вы можете использовать IQueryable и принять, что кто-то может создать сценарий, в котором может произойти SELECT N + 1 . Это недостаток, а также тот факт, что вы можете получить код, специфичный для вашей реализации репозитория, на уровнях выше вашего репозитория. Преимущество этого состоит в том, что вы позволяете делегировать общие операции, такие как разбиение на страницы и сортировка, за пределами своего репозитория, что облегчает такие проблемы. Это также более гибко, если вам нужно объединить данные с другими таблицами базы данных, поскольку запрос останется выражением, поэтому его можно добавить до того, как он будет преобразован в запрос и попадет в базу данных.

Альтернатива - заблокировать ваш репозиторий, чтобы он возвращал материализованные списки, вызывая ToList(). На примере разбивки на страницы и сортировки вам нужно будет передать в качестве параметров методы вашего репозитория параметры skip, take и sort, а также использовать параметры для возврата только окна результатов. Это означает, что хранилище берет на себя ответственность за разбиение на страницы и сортировку, а также за всю проекцию ваших данных.

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

...