Должен ли я возвращать 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 и имеете меньшую сложность в репозитории, или вы контролируете доступ к данным. Для меня это зависит от количества запросов, связанных с каждой сущностью, и от комбинаций объектов, и где я хочу управлять этой сложностью.