.NET Entity Framework - IEnumerable VS.IQueryable - PullRequest
23 голосов
/ 30 сентября 2011

Пожалуйста, посмотрите эту строку кода. Это вызов хранимой процедуры, которая возвращает ObjectResult<long?>. Чтобы извлечь длинные значения, я добавил Select:

dbContext.FindCoursesWithKeywords(keywords).Select(l => l.Value);

На основании intellisense этот выбор возвращает IEnumerable<long>.

Я не уверен, что я где-то читал или, может быть, просто привык к этому предположению - я всегда думал, что когда EF API возвращает IEnumerable (а не IQueryable), то это означает, что результаты были материализовались. Это означает, что они были извлечены из базы данных.

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

"Новая транзакция не разрешена, потому что есть другие потоки работает в сеансе "

По сути, эта ошибка говорит о том, что вы пытаетесь сохранить изменения, пока программа чтения БД все еще читает записи.

В конце концов я решил ее (что я считал длинным выстрелом) и добавил ToArray() вызов, чтобы материализовать IEnumerable<long> ...

Итак, нижняя строка - следует ли ожидать, что IEnumerable результаты от EF будут содержать результаты, которые еще не материализовались? Если да, то есть ли способ узнать, материализован ли IEnumerable или нет?

Спасибо и прошу прощения, если это один из тех «духовых» вопросов ...:)

Ответы [ 4 ]

28 голосов
/ 30 сентября 2011

IQueryable используется, когда вы используете Linq-to-entity = вы создаете декларативный запрос LINQ в вашем приложении, который будет интерпретирован поставщиком LINQ как SQL и выполнен на сервере.Как только запрос будет выполнен (повторен), он превратится в IEnumerable, и объекты будут материализованы как необходимые для итерации = не сразу.

После вызова хранимой процедуры вы не используете Linq-to-entity, поскольку существуетв вашем приложении нет встроенного декларативного запроса.Запрос / SQL уже существует на сервере базы данных, и вы просто вызываете его.Это вернет IEnumerable, но опять же не сразу материализует все результаты.Результаты будут реализованы как повторяющиеся.Это принцип работы курсора базы данных или чтения данных .NET, когда вы явно запрашиваете выборку объекта.

Так что если вы вызываете что-то вроде этого:

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
                                 .Select(l => l.Value))
{
    ...   
}

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

Если вы вместо этого вызовете это:

foreach (var keyword in dbContext.FindCoursesWithKeywords(keywords)
                                 .ToList() // or ToArray 
                                 .Select(l => l.Value))
{
    ...
}

Вы заставите запрос немедленно реализовать все результаты, и цикл будет выполнятьсяпри сборе в памяти вместо открытого считывателя базы данных.

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

11 голосов
/ 30 сентября 2011

Работа с IEnumerable<T> означает, что все дальнейшие операции будут выполняться в коде C #, т.е. linq-to-objects.Это не означает, что запрос уже выполнен.

После перехода к linq-to-objects все данные, оставшиеся в этой точке, должны быть извлечены из базы данных и отправлены в .net.Это может резко снизить производительность (например, индексы базы данных не будут использоваться linq-to-objects), но, с другой стороны, linq-to-objects более гибок, поскольку может выполнять произвольный код C # вместо того, чтобы ограничиватьсяваш поставщик linq может перевести на SQL.

A IEnumerable<T> может быть как отложенным запросом, так и уже материализованными данными.Стандартные операторы linq обычно откладываются, и ToArray() / ToList() всегда материализуются.

0 голосов
/ 04 августа 2016

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

0 голосов
/ 19 апреля 2012

IEnumerable: LINQ to Object и LINQ to XML.

IQueryable: LINQ to SQL

...