Entity Framework - производительность в счете
У меня вопрос о производительности с Entity Framework.
Что-то вроде
using (MyContext context = new MyContext())
{
Document DocObject = context.Document.Find(_id);
int GroupCount = context.Document.Where(w=>w.Group == DocObject.Group).ToList().Count();
}
занимает около 2 секунд в моей базе данных (около 30 тыс. наборов данных), в то время как этот
using (MyContext context = new MyContext())
{
Document DocObject = context.Document.Find(_id);
int GroupCount = context.Document.Where(w=>w.Group == DocObject.Group).Count();
}
принимает 0,02 секунды.
Когда у моего фильтра для 10 документов осталось 20 секунд, я проверил свой код и изменил его, чтобы не использовать ToList()
до Count()
.
Любые идеи, почему для этой строки требуется 2 секунды с ToList()
?
Ответы
Ответ 1
Вызов ToList()
, затем Count()
будет:
- выполнить все
SELECT FROM WHERE
по вашей базе данных
- затем материализуют все результирующие объекты как объекты .Net
- создать новый объект
List<T>
, содержащий все результаты
- возвращает результат свойства
Count
только что созданного списка .Net.
Вызов Count()
против IQueryable
будет:
- выполнить
SELECT COUNT FROM WHERE
по вашей базе данных
- возвращает
Int32
число строк
Очевидно, что если вас интересует только количество элементов (не сами элементы), вы никогда не должны сначала звонить ToList()
, так как для этого потребуется много ресурсов для ничего.
Ответ 2
Да, ToList()
будет оценивать результаты (извлекать объекты из базы данных), если вы не используете ToList()
, объекты не извлекаются из базы данных.
Linq-To-Entities использует LazyLoading по умолчанию.
Он работает примерно так:
Когда вы запрашиваете базовое соединение с БД с помощью Linq-To-Entities, вы получите прокси-объект, на котором вы можете выполнить несколько операций (считая одним). Это означает, что вы не сразу получаете все данные из БД, а объекты извлекаются из БД во время оценки. Одним из способов оценки объекта является использование ToList().
Возможно, вам стоит прочитать this.
Ответ 3
Потому что ToList()
будет запрашивать базу данных для всех объектов (будет делать SELECT *
так сказать), а затем вы будете использовать Count()
в списке в памяти со всеми записями, тогда как если вы используете Count()
на IQueryable
(а не на List
), EF переведет его в простой SELECT COUNT(*)
SQL-запрос
Ответ 4
Ваш первый запрос не полностью транслируется на sql - когда вы вызываете .ToList().Count()
, вы в основном говорите "загружайте все, материализуйте его в POCO и метод расширения с именем Count()
", что, конечно, займет некоторое время.
Второй запрос, однако, транслируется на что-то вроде select count(*) from Documents where GroupId = @DocObjectGroup
, которое намного быстрее выполняется, и вы arent материализуете что-либо, просто простое скалярное.
Ответ 5
Использование метода расширения Enumerable.ToList()
построит новый объект List из коллекции источников IEnumerable<T>
, что означает, что с помощью ToList()
есть связанная с этим стоимость.