Возвращая IEnumerable <T>против IQueryable <T> - PullRequest
1017 голосов
/ 20 мая 2010

В чем разница между возвращением IQueryable<T> против IEnumerable<T>?

IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;

Будут ли оба варианта отложены, и когда один из них предпочтительнее другого?

Ответы [ 15 ]

10 голосов
/ 22 января 2015

Я хотел бы уточнить некоторые вещи из-за, казалось бы, противоречивых ответов (в основном из-за IEnumerable).

(1) IQueryable расширяет интерфейс IEnumerable. (Вы можете отправить IQueryable на то, что ожидает IEnumerable без ошибок.)

(2) И IQueryable, и IEnumerable LINQ пытаются выполнить отложенную загрузку при итерации по набору результатов. (Обратите внимание, что реализацию можно увидеть в методах расширения интерфейса для каждого типа.)

Другими словами, IEnumerables не являются исключительно "в памяти". IQueryables не всегда выполняются в базе данных. IEnumerable должен загружать вещи в память (после извлечения, возможно, лениво), потому что у него нет поставщика абстрактных данных. IQueryables полагается на абстрактный поставщик (например, LINQ-to-SQL), хотя это также может быть поставщик .NET в памяти.

Пример использования

(a) Получить список записей как IQueryable из контекста EF. (Нет записей в памяти.)

(b) Передайте IQueryable представлению, модель которого IEnumerable. (Действителен. IQueryable расширяется IEnumerable.)

(c) Выполните итерацию и получите доступ к записям набора данных, дочерним объектам и свойствам из представления. (Может вызвать исключения!)

Возможные проблемы

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

(2) Прокси-объекты сущностей Entity Framework включены (по умолчанию), и вы пытаетесь получить доступ к связанному (виртуальному) объекту с контекстом данных с истекшим сроком действия. То же, что (1).

(3) Несколько активных наборов результатов (MARS). Если вы выполняете итерацию по IEnumerable в блоке foreach( var record in resultSet ) и одновременно пытаетесь получить доступ к record.childEntity.childProperty, вы можете получить MARS из-за отложенной загрузки набора данных и реляционной сущности. Это вызовет исключение, если оно не включено в строке подключения.

Решение

  • Я обнаружил, что включение MARS в строке подключения работает ненадежно. Я предлагаю вам избегать MARS, если это не является понятным и явно желательным.

Выполните запрос и сохраните результаты, вызвав resultList = resultSet.ToList() Похоже, это самый простой способ убедиться, что ваши объекты находятся в памяти.

В случаях, когда вы обращаетесь к связанным сущностям, вам все равно может потребоваться контекст данных. Либо так, либо вы можете отключить прокси сущностей и явно Include связанные сущности из вашего DbSet.

9 голосов
/ 09 мая 2016

Основное различие между «IEnumerable» и «IQueryable» заключается в том, где выполняется логика фильтра. Один выполняется на стороне клиента (в памяти), а другой - в базе данных.

Например, мы можем рассмотреть пример, где у нас есть 10000 записей для пользователя в нашей базе данных, и, скажем, только 900 из них являются активными пользователями, поэтому в этом случае, если мы используем «IEnumerable», то сначала он загружает все 10000 записей в памяти, а затем применяет фильтр IsActive, который в итоге возвращает 900 активных пользователей.

С другой стороны, в том же случае, если мы используем «IQueryable», он напрямую применяет фильтр IsActive к базе данных, которая непосредственно оттуда вернет 900 активных пользователей.

Ссылка Ссылка

5 голосов
/ 22 мая 2017

В дополнение к первым двум действительно хорошим ответам (автор driis & Jacob):

IEnumerable интерфейс находится в пространстве имен System.Collections.

Объект IEnumerable представляет набор данных в памяти и может перемещаться по этим данным только вперед. Запрос, представленный объектом IEnumerable, выполняется немедленно и полностью, поэтому приложение быстро получает данные.

Когда запрос выполняется, IEnumerable загружает все данные, и если нам нужно отфильтровать их, сама фильтрация выполняется на стороне клиента.

Интерфейс IQueryable находится в пространстве имен System.Linq.

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

Что выбрать?

Если вам нужен весь набор возвращаемых данных, то лучше использовать IEnumerable, который обеспечивает максимальную скорость.

Если вам НЕ нужен весь набор возвращаемых данных, а только некоторые отфильтрованные данные, то лучше использовать IQueryable.

5 голосов
/ 25 марта 2016

Мы можем использовать оба для одного и того же способа, и они отличаются только по производительности.

IQueryable эффективно работает только с базой данных. Это означает, что он создает весь запрос выбора и получает только связанные записи.

Например, мы хотим взять топ-10 клиентов, чье имя начинается с «Nimal». В этом случае запрос на выборку будет сгенерирован как select top 10 * from Customer where name like ‘Nimal%’.

Но если бы мы использовали IEnumerable, запрос был бы похож на select * from Customer where name like ‘Nimal%’, и первая десятка будет отфильтрована на уровне кодирования C # (он получает все записи клиентов из базы данных и передает их в C #).

0 голосов
/ 02 февраля 2019

IEnumrable будет хранить данные в памяти

Но в случае Iqueruable он не сохраняется в памяти

Для более подробной информации вы проверяете с помощью sql profiler

Первое время ударил тебя запрос с IQueryable И посмотри, какой запрос выполнить

Тогда попробуйте из IEnumrable

...