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

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

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

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

Как для использования с шаблоном репозитория, я считаю, что он нетерпелив. Я обычно вижу, что Илис передают, но кому-то еще нужно будет это угадать. EDIT - вы обычно видите IEnumerable вместо IQueryable, потому что вам не нужны слои, находящиеся за вашим репозиторием A), определяющие, когда произойдет сбой базы данных, или B) Добавление любой логики к объединениям вне репозитория

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

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

Ответ 4

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

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

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