Я не понимаю LINQ to SQL.AsEnumerable()?

Рассмотрим этот код:

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 извлекать записи из базы данных. Когда вы выполняете исходящие вычисления, они выполняются с объектами памяти, а не с использованием базы данных.

Выход из возвращаемого типа в качестве Перечислимого означает, что данные не извлекаются до тех пор, пока он не будет вызван кодом, выполняющим вычисления. Я думаю, что это связано с тем, что база данных попадает три раза - по одному для каждого вычисления и данные не сохраняются в памяти.