Багато було сказано раніше, але повернемось до коріння більш технічним способом:
IEnumerable
це сукупність об'єктів в пам'яті, які ви можете перерахувати - послідовність в пам'яті, яка дозволяє повторити процес (полегшує проходження в foreach
циклі, хоча ви можете перейти IEnumerator
лише з ним). Вони залишаються в пам'яті як є.
IQueryable
це дерево виразів, яке в якийсь момент буде перетворене на щось інше з можливістю перерахувати над кінцевим результатом . Я думаю, саме це бентежить більшість людей.
Вони, очевидно, мають різні конотації.
IQueryable
являє собою дерево виразів (запит, просто), яке буде переведено на щось інше, що лежить в основі постачальника запитів, як тільки викликаються API випуску, наприклад, функції сукупності LINQ (сума, підрахунок тощо) або 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
радимо використовувати лише в сховищах і IE чимало в іншому місці коду. Не кажучи про проблеми, пов'язані з встановленням тестів, які IQueryable
руйнуються та руйнують принцип поділу проблем . Якщо ви повернете вираз із сховищ, споживачі можуть грати зі стійким шаром, як хотіли б.
Невелике доповнення до безладу :) (з дискусії в коментарях)) Жоден з них не є об’єктами в пам'яті, оскільки вони самі по собі не є реальними типами, вони є маркерами типу - якщо ви хочете заглибитись так глибоко. Але має сенс (і тому навіть MSDN так вважає) думати про IEnumerables як колекції пам'яті, тоді як IQueryables - як дерева виразів. Справа в тому, що інтерфейс IQueryable успадковує інтерфейс IEnumerable, щоб, якщо він представляє запит, результати цього запиту можна було перерахувати. Перерахування спричиняє виконання експресійного дерева, пов'язаного з об'єктом IQueryable. Отже, насправді ви не можете дійсно зателефонувати жодному члену IEnumerable, не маючи об’єкта в пам'яті. Він потрапить туди, якщо ви все одно, якщо він не порожній. IQueryables - це лише запити, а не дані.