Рассмотрим этот код:
var query = db.Table
.Where(t => SomeCondition(t))
.AsEnumerable();
int recordCount = query.Count();
int totalSomeNumber = query.Sum();
decimal average = query.Average();
Предположим, что query
занимает очень много времени. Мне нужно вернуть счетчик записей, итого SomeNumber
и взять среднее значение в конце. Я подумал, что, основываясь на моем чтении, .AsEnumerable()
выполнит запрос с использованием LINQ-to-SQL, затем используйте LINQ-to-Objects для Count
, Sum
и Average
. Вместо этого, когда я это делаю в LINQPad, я вижу, что один и тот же запрос запускается три раза. Если я заменю .AsEnumerable()
на .ToList()
, он будет запрашиваться только один раз.
Мне не хватает чего-то о том, что AsEnumerable
есть/делает?
Ответ 1
Вызов AsEnumerable(
) не выполняет запрос, перечисляя его.
IQueryable
- это интерфейс, который позволяет LINQ to SQL
выполнять свою магию. IQueryable
реализует IEnumerable
, поэтому, когда вы вызываете AsEnumerable()
, вы меняете вызовы-расширения, вызываемые оттуда, то есть от методов IQueryable
до методов IEnumerable
(т.е. меняющихся от LINQ to SQL
до LINQ to Objects
в данном конкретном случае). Но вы не выполняете фактический запрос, просто изменяя , как он будет выполнен полностью.
Чтобы принудительно выполнить выполнение запроса, вы должны вызвать ToList()
.
Ответ 2
Да. Все, что AsEnumerable
будет делать, это вызвать выполнение функций Count
, Sum
и Average
на стороне клиента (другими словами, он вернет весь результат для клиента, тогда клиент будет выполнять эти агрегаты вместо создания COUNT()
SUM()
и AVG()
операторов в SQL).
Ответ 3
Ну, ты на правильном пути. Проблема в том, что IQueryable
(то, что оператор перед вызовом AsEnumerable
) также является IEnumerable
, так что вызов, по сути, является nop. Для принудительного запроса потребуется принудительная привязка к определенной структуре данных в памяти (например, ToList()
).
Ответ 4
Ответ Джастина Нисснера идеально.
Я просто хочу процитировать объяснение MSDN здесь: . NET-интегрированный запрос для реляционных данных
Оператор AsEnumerable(), в отличие от ToList() и ToArray(), не вызывает выполнение запроса. Он все еще отложен. Оператор AsEnumerable() просто изменяет статическую типизацию запроса, превращая IQueryable в IEnumerable, обманывая компилятор, рассматривая остальную часть запроса как локально выполненную.
Надеюсь, именно это подразумевается под:
IQueryable-методы для IEnumerable-методов (т.е. переход от LINQ to SQL к LINQ to Objects
Как только LINQ to Objects можно применить методы объекта (например, ToString()). Это объяснение одного из часто задаваемых вопросов о LINQ - Почему LINQ to Entities не распознает метод" System.String ToString()?
Согласно ASENUMERABLE - codeblog.jonskeet, AsEnumerable
может быть удобно, если:
некоторые аспекты запроса в базе данных, а затем немного больше манипуляций в .NET - особенно если есть аспекты, которые вы в основном не реализуете в LINQ to SQL (или любой другой поставщик, который вы используете).
В нем также говорится:
Все делали это изменение типа времени компиляции последовательности, которая распространяется по нашему запросу от IQueryable до IEnumerable, но это означает, что компилятор будет использовать методы в Enumerable (беря делегаты и выполняя LINQ to Objects) вместо тех, которые находятся в Queryable (берущие деревья выражений и обычно выполняющиеся вне процесса).
Наконец, также см. этот связанный вопрос: Возврат IEnumerable против IQueryable
Ответ 5
Я бы предположил, что ToList заставляет Linq извлекать записи из базы данных. Когда вы выполняете исходящие вычисления, они выполняются с объектами памяти, а не с использованием базы данных.
Выход из возвращаемого типа в качестве Перечислимого означает, что данные не извлекаются до тех пор, пока он не будет вызван кодом, выполняющим вычисления. Я думаю, что это связано с тем, что база данных попадает три раза - по одному для каждого вычисления и данные не сохраняются в памяти.