.NET Entity Framework-IEnumerable VS. IQueryable

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

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

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

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

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

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

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

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

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

спасибо и извинения, если это один из тех duhhh вопросы... :)

4 ответов


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

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

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

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. Разница заключается в поддержке конструкции (что-то должно реализовать этот интерфейс.)


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

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

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


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


IEnumerable: LINQ для объекта и LINQ для XML.

IQueryable: LINQ to SQL