Многое было сказано ранее, но вернемся к корням более техническим способом:
IEnumerable
это коллекция объектов в памяти, которую вы можете перечислить - последовательность в памяти, которая позволяет выполнять итерации (упрощает foreach
цикл в цикле, хотя вы можете работать IEnumerator
только с ним). Они хранятся в памяти как есть.
IQueryable
это дерево выражений , которое в какой-то момент преобразуется во что-то другое с возможностью перечисления окончательного результата . Я думаю, это то, что смущает большинство людей.
Они, очевидно, имеют разные значения.
IQueryable
представляет собой дерево выражений (просто запрос), которое будет преобразовано во что-либо еще базовым поставщиком запросов при вызове API-интерфейсов выпуска, например агрегатных функций LINQ (Sum, Count и т. д.) или ToList [Array, Dictionary ,. ..]. И IQueryable
объекты также реализуют IEnumerable
, IEnumerable<T>
так что если они представляют запрос, результат этого запроса может быть повторен. Это означает, что IQueryable не должен быть только запросом. Правильный термин - это деревья выражения .
Теперь то, как эти выражения выполняются и к чему они обращаются, зависит от так называемых поставщиков запросов (исполнителей выражений мы можем думать о них).
В мире Entity Framework (который является мистическим базовым поставщиком источника данных или поставщиком запросов) IQueryable
выражения транслируются в собственные запросы T-SQL . Nhibernate
делает подобные вещи с ними. Вы можете написать свой собственный, следуя принципам, довольно хорошо описанным в LINQ: например, создание ссылки на провайдера IQueryable , и вы можете захотеть иметь пользовательский API запросов для службы поставщика вашего магазина продуктов.
Таким образом, в основном IQueryable
объекты создаются все время, пока мы явно не освободим их и не попросим систему переписать их в SQL или что-то еще и отправить цепочку выполнения для дальнейшей обработки.
Что же касается отложенного выполнения, то это LINQ
возможность удерживать схему дерева выражений в памяти и отправлять ее в выполнение только по требованию, всякий раз, когда определенные API-интерфейсы вызываются для последовательности (тот же Count, ToList и т. Д.).
Правильное использование обоих в значительной степени зависит от задач, стоящих перед вами в конкретном случае. Для хорошо известного шаблона репозитория я лично предпочитаю возвращать IList
, то есть IEnumerable
списки (индексаторы и тому подобное). Поэтому мой совет использовать IQueryable
только в репозиториях и IEnumerable где-либо еще в коде. Не говоря о проблемах, связанных с проверяемостью, которые IQueryable
разрушают и разрушают принцип разделения интересов . Если вы возвращаете выражение из репозиториев, потребители могут поиграть со слоем персистентности так, как им хотелось бы.
Небольшое дополнение к беспорядку :) (из обсуждения в комментариях)) Ни один из них не является объектами в памяти, так как они не являются реальными типами как таковыми, это маркеры типа - если вы хотите углубиться в это. Но имеет смысл (и именно поэтому даже MSDN так выразился) рассматривать IEnumerables как коллекции в памяти, а IQueryables - как деревья выражений. Дело в том, что интерфейс IQueryable наследует интерфейс IEnumerable, так что, если он представляет запрос, результаты этого запроса могут быть перечислены. Перечисление вызывает выполнение дерева выражений, связанного с объектом IQueryable. Таким образом, на самом деле вы не можете вызвать любой член IEnumerable, не имея объекта в памяти. В любом случае он попадет туда, если вы это сделаете, если он не пустой. IQueryables - это просто запросы, а не данные.