Должен ли я возвращать IEnumerable‹T› или IQueryable‹T› из моего DAL?

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

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

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

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

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

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

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>? Какие преимущества/недостатки есть у того или другого?


person Armstrongest    schedule 14.06.2010    source источник


Ответы (4)


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

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

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

person Jim Lamb    schedule 14.06.2010
comment
Право на. Я всегда смешивал IList и IEnumerable в одну кучу, но эта идея мне нравится больше. - person scottm; 16.12.2010

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

person Ryan    schedule 17.06.2010

ОГРОМНАЯ разница. Я вижу это совсем немного.

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

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

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

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

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

person Mike M.    schedule 14.06.2010
comment
Просто для ясности: ProductRepository находится внутри моего приложения... тогда как DAL - это отдельный проект, на который у меня есть ссылка. Итак, я вызываю репозиторий из своего приложения, репозиторий вызывает класс DBDataContext в проекте DAL (который содержит все общие методы), который затем выполняет любой LinqToSQL для базы данных. - person Armstrongest; 14.06.2010
comment
IEnumerable не обязательно стремится. Enumerable.Select возвращает ленивый IEnumerable. - person Amy B; 14.06.2010
comment
Отличное видео. Спасибо за это! - person Armstrongest; 22.06.2010

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

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

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

person Peter Willis    schedule 14.06.2010
comment
Хм... есть над чем подумать. - person Armstrongest; 15.06.2010