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() есть связанная с этим стоимость.