Я пытаюсь понять, что такое IEnumerable<>
.
Я думал, что это просто интерфейс, говорящий, что базовая структура данных имеет эти методы, например First()
и т. д.
Однако я протестировал показанный здесь код в EF, который, как я ожидал, создаст запрос TOP в БД, как если бы он выполнялся как IQueryable
, но вместо этого он заставляет его извлекать всю таблицу в памяти для выполнения своей операции.
IEnumerable<EntityObject> m = DbContext.GetDbSet<EntityObject>();
var x = m.First();
Так заставляет ли обработка чего-либо как IEnumerable
работать как массив, список или что-то в этом роде?
Примечание. Мне НЕ нужно знать, как оптимизировать запрос, чтобы он был более производительным. Я пытаюсь понять, почему просто рассматривать его как другой интерфейс и использовать одни и те же методы приводит к другой базовой логике.
Перечисляемый объект может позволить вам выполнять бесконечные итерации, например: static IEnumerable<int> Values() { for (int i = 0; ; ++i) { yield return i; } }
«Что такое IEnumerable<>» и то, что EF делает для его поддержки, — это две совершенно разные вещи.
Попробуйте m = DbContext.GetDbSet<EntityObject>().Take(1);
@HenkHolterman «Что такое IEnumerable<>» и то, что EF делает для его поддержки, - это две разные вещи». IEnumerable и IQueryable имеют операцию First, я бы подумал, что каким бы ни был тип данных EF внизу, интерфейсы будут вызывая одни и те же операции First, делая то же самое. Как структура данных EF может делать что-то еще на основе одного и того же вызова метода разными интерфейсами?
m = DbContext.GetDbSet<EntityObject>().Take(1); это будет делать top(1), потому что он не обрабатывается как IEnumerable, я это уже понимаю, это будет то же самое, как если бы мы сделали first или что-то еще. Я не пытаюсь генерировать эффективный sql, я просто пытаюсь понять, что такое IEnumerable, поскольку он не соответствует тому, как, по моему мнению, он должен работать.
Вы должны спросить о разнице между IEnumerable
и IQueryable
medium.com/@shahrukhkhon_7802/…IQueryable
методы фиксируют ваше намерение, чтобы его можно было преобразовать в sql, IEnumerable
методы оптимизированы для выполнения в памяти. Отношение к IQueryable
как к IEnumerable
уничтожит его преимущества.
.Net IEnumerable, что это такое?
Как @ProgrammingLlama и @Henk Holterman уже дали вам первоначальное представление, однако я пытаюсь расширить его немного больше.
Как вы, возможно, знаете, IEnumerable — это базовый интерфейс для всех неуниверсальных коллекций, которые можно перечислять, таких как массивы, списки или любые другие коллекции, реализующие IEnumerable. Основная функциональность, предоставляемая IEnumerable, — это возможность перебирать коллекцию с помощью цикла foreach.
То же самое касается и того, что что-то рассматривается как IEnumerable, что заставляет его работать. как массив или список или что-то в этом роде?
На самом деле, простое присвоение IQueryable<T>
IEnumerable<T>
не приводит к немедленному выполнению запроса и загрузке данных в память. Именно последующие операции, такие как вызов First(), вызывают выполнение запроса и загрузку данных в память.
Ключевой момент заключается в том, что хотя само присваивание не вызывает немедленного выполнения, последующие операции, выполняемые над IEnumerable, будут операциями в памяти, а некоторые операции, такие как First(), действительно будут выполнять запрос и загружать данные. Любые последующие операции, например Where
, First
, Select
, выполняются в памяти.
Теперь вернемся к вашему общему фрагменту кода. Чтобы гарантировать, что операция First()
выполняется в базе данных, а не в памяти, вам следует сохранить коллекцию как IQueryable. Таким образом, метод First()
будет преобразован в SQL-запрос с предложением TOP 1, и из базы данных будет получена только первая сущность.
Итак, вы можете изменить свой код следующим образом:
IQueryable<EntityObject> m = DbContext.GetDbSet<EntityObject>();
var x = m.First();
Примечание. Пожалуйста, обратитесь к этому официальному документу за дополнительным примером
«На самом деле, это заставляет запрос выполняться немедленно и загружать результаты в память». Нет, это не так. Просто присвоение переменной типа IEnumerable<T>
этого не сделает. Когда вы используете данные, представленные IEnumerable<T>
, эти данные будут принудительно загружены. Да, дальнейшие операции, такие как Where
и т. д., будут выполняться в памяти, но это не означает, что данные загружаются в тот момент, когда выполняется присвоение IEnumerable<T>
.
@JonSkeet Рад получить ваше мнение по этому поводу, я бы обновил свой ответ.
Гораздо лучше, спасибо за обновление.
Спасибо @JonSkeet за то, что указали на это, и ваше предложение — благословение для меня. Прошло 8 лет с тех пор, как я слежу за вами, вашим блогом, github.
Пришлось немного покопаться в методах декомпилятора. Насколько я могу судить, причина разницы в функциональности на самом деле связана с тем, что IEnumerables и IQueryables имеют разные методы расширения.
Похоже, в самих интерфейсах мало что происходит. Это объясняет мои первоначальные опасения, поскольку я не мог понять, как простое обращение с ним как с другим интерфейсом может повлиять на выполняемые над ним операции.
Методы расширения IQueryables заставляют его выполнять выражения, которые ему необходимо запросить, тогда как выражения Enumerable предлагают использовать GetEnumerator и перемещаться по ним с помощью MoveNext(), что, по-видимому, инициирует запрос при первом вызове.
Да, хорошая попытка, однако я имел в виду именно это. В любом случае, рад видеть, что у вас это тоже есть.
Если что-то является IEnumerable, вы можете использовать его в цикле foreach. Другими словами, вы можете перечислить это.