LINQ to Entities - OrderBy().ToLisT() или ToList().OrderBy()
Я ищу подтверждение / уточнение с этими выражениями LINQ:
var context = new SomeCustomDbContext()
// LINQ to Entities?
var items = context.CustomItems.OrderBy(i => i.Property).ToList();
// LINQ to Objects?
var items2 = context.CustomItems.ToList().OrderBy(i => i.Property);
(Q1) Правильно ли я считаю, что первый метод LINQ to Entities
где EF создает более конкретный оператор SQL для передачи, упорядочивая базу данных?
(Q2) это второй метод LINQ to Objects
где LINQ перетаскивает всю коллекцию в память (ToList()
перечисление?) перед заказом, таким образом, оставляя бремя на стороне сервера (веб-сервер в данном случае)?
Если это так, я могу быстро увидеть ситуации, в которых L2E был бы выгоден (например, фильтрация / обрезка коллекций перед вытягиванием их в память).
(Q3) Но есть ли какие-либо другие детали / компромиссы, о которых я должен знать, или времена, когда "метод 2" может быть выгоднее первого метода?
ОБНОВИТЬ:
Допустим, мы не используем EntityFramework, это все еще верно, пока базовый репозиторий / источник данных реализует IQueryable<T>
право? И если это не так, оба эти заявления приводят к LINQ to Objects
операции в памяти?
3 ответа
- Да.
- Да.
- Да.
Вы правы в том, что зовете ToList()
заставляет linq-to-entity оценивать и возвращать результаты в виде списка. Как вы подозреваете, это может иметь огромное влияние на производительность.
Есть случаи, когда linq-to-entity не может понять, как анализировать то, что выглядит как совершенно простой запрос (например, Where(x => SomeFunction(x))
). В этих случаях у вас часто нет другого выбора, кроме как позвонить ToList()
и оперировать коллекцией в памяти.
В ответ на ваше обновление:
ToList()
всегда заставляет все, что впереди, оценивать немедленно, в отличие от отложенного исполнения. Возьмите этот пример:
someEnumerable.Take(10).ToList();
против
someEnumerable.ToList().Take(10);
Во втором примере любая отложенная работа на someEnumerable
должен быть выполнен перед тем, как взять первые 10 элементов. Если someEnumerable
делает что-то трудоемкое (например, чтение файлов с диска с помощью Directory.EnumerateFiles()
), это может иметь очень реальные последствия для производительности.
Правильно ли я считаю, что первый метод - это LINQ to Entities, где EF создает более конкретный оператор SQL для передачи, упорядочивая базу данных?
да
Является ли второй метод LINQ to Objects, где LINQ перетаскивает всю коллекцию в память... перед упорядочением, оставляя бремя на стороне сервера...?
да
Но есть ли какие-либо другие детали / компромиссы, о которых я должен знать, или случаи, когда "метод 2" может быть выгоднее первого метода?
Во многих случаях метод 1 будет невозможен - обычно, когда у вас есть сложный фильтр или порядок сортировки, который не может быть напрямую переведен в SQL (или более уместно, когда EF не поддерживает прямую трансляцию SQL). Кроме того, так как вы не можете передавать лениво загружен IQueryable
s по проводам, каждый раз, когда вам нужно сериализовать результат, вы должны сначала его материализовать ToList()
или что-то подобное.
Еще одна вещь, о которой следует знать, - это то, что IQueryable не дает никаких гарантий ни (а) о семантических рассуждениях базового поставщика, либо (b) о том, какая часть набора методов IQueryable реализована поставщиком.
Например: -
- EF не поддерживает Last().
- Он также не поддерживает сравнение временных дат DateTimes с действительным T-SQL.
- Он не поддерживает FirstOrDefault() в подзапросах.
В таких обстоятельствах вам необходимо вернуть данные клиенту, а затем выполнить дальнейшую оценку на стороне клиента.
Вам также необходимо иметь представление о том, "как" он анализирует конвейер LINQ для генерации (в случае EF) T-SQL. Поэтому иногда вам нужно тщательно продумать, как вы строите свои запросы LINQ, чтобы генерировать эффективный T-SQL.
Тем не менее, IQueryable<> является чрезвычайно мощным инструментом в среде.NET, и с ним стоит познакомиться поближе.