Рассмотрите этот код:
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-SQL, затем использовать LINQ к объектам для Count
, Sum
, и Average
. Вместо этого когда я делаю это в LINQPad, я вижу, что тот же запрос выполняется три раза. Если я заменяю .AsEnumerable()
с .ToList()
, это только запрашивается однажды.
Я пропускаю что-то о какой AsEnumerable
/?
Вызов AsEnumerable (
) не выполняет запрос, а перечисление выполняет.
IQueryable
- это интерфейс, который позволяет LINQ to SQL
творить чудеса. IQueryable
реализует IEnumerable
, поэтому, когда вы вызываете AsEnumerable ()
, вы меняете методы расширения, вызываемые оттуда, то есть из IQueryable
-методы к IEnumerable
-методы (то есть переход с LINQ to SQL
на LINQ to Objects
в данном конкретном случае). Но вы не выполняете фактический запрос, а просто меняете то, как он будет выполняться полностью.
Чтобы принудительно выполнить запрос, необходимо вызвать ToList ()
.
Я предполагаю, что ToList заставляет Linq получить записи из базы данных. Когда вы затем выполняете дальнейшие вычисления, они производятся с объектами в памяти, а не с базой данных.
Если оставить тип возврата как Enumerable, это означает, что данные не извлекаются до тех пор, пока к ним не обратится код, выполняющий вычисления. Я полагаю, что это приводит к тому, что к базе данных обращаются три раза - по одному для каждого вычисления, и данные не сохраняются в памяти.
Да. Все, что сделает AsEnumerable
, - это вызовет выполнение функций Count
, Sum
и Average
на стороне клиента (другими словами, он вернет клиенту весь набор результатов, затем клиент будет выполнять эти агрегаты вместо создания операторов COUNT ()
SUM ()
и AVG ()
в SQL. ).
Что ж, вы на правильном пути. Проблема в том, что IQueryable
(то, что было перед вызовом AsEnumerable
) также является IEnumerable
, так что этот вызов, по сути, является nop. Для принудительного выполнения запроса потребуется принудительно использовать определенную структуру данных в памяти (например, ToList ()
).