Подтвердить что ты не робот

.NET Entity Framework - IEnumerable VS. IQueryable

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

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

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

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

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

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

В основном, эта ошибка говорит вам, что вы пытаетесь сохранить изменения, пока читатель db все еще читает записи.

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

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

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

4b9b3361

Ответ 1

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

Как только вы вызываете хранимую процедуру, вы не используете Linq-to-entity, потому что в вашем приложении нет декларативного запроса. Запрос /SQL уже существует на сервере базы данных, и вы просто вызываете его. Это вернет IEnumerable, но снова это не принесет никаких результатов сразу. Результаты будут реализованы как итерационные. Это принцип чтения/чтения данных в формате HTML/.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. Разница заключается в поддержке конструкции (что-то должно реализовывать эти интерфейсы).

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

IEnumerable: LINQ to Object и LINQ to XML.

IQueryable: LINQ to SQL