LINQ ToList (). Take (10) против Take (10) .ToList (), который генерирует более эффективный запрос - PullRequest
8 голосов
/ 08 декабря 2010

Учитывая следующие операторы LINQ, что будет более эффективным?

ONE:

public List<Log> GetLatestLogEntries()
{
    var logEntries = from entry in db.Logs
                 select entry;
    return logEntries.ToList().Take(10);
}

TWO:

public List<Log> GetLatestLogEntries()
{
    var logEntries = from entry in db.Logs
                 select entry;
    return logEntries.Take(10).ToList();
}

Мне известно, что .ToList () немедленно выполняет запрос.

Ответы [ 5 ]

20 голосов
/ 08 декабря 2010

Первая версия даже не скомпилируется - потому что возвращаемое значение Take равно IEnumerable<T>, а не List<T>.Таким образом, вам нужно, чтобы это было:

public List<Log> GetLatestLogEntries()
{
    var logEntries = from entry in db.Logs
                 select entry;
    return logEntries.ToList().Take(10).ToList();
}

Это будет извлекать все данные из базы данных и преобразовывать их в список, затем взять первые 10 записей, затем снова преобразуйте его в список.

Получение Take(10) в базе данных (т. е. во второй форме) определенно выглядит для меня намного дешевле ...

Обратите внимание, что метода Queryable.ToList() нет - в итоге вы вызовете Enumerable.ToList(), который будет извлекать все записи.Другими словами, вызов ToList не не участвует в переводе SQL, тогда как Take делает.

Также обратите внимание, что использование здесь выражения запроса не имеет большого смыслаили.Я бы написал так:

public List<Log> GetLatestLogEntries()
{
    return db.Log.Take(10).ToList();
}

Имейте в виду, вы можете захотеть позвонить OrderBy - иначе он просто примет первые 10 найденных записей, которые могут быть не самыми последними ...

2 голосов
/ 08 декабря 2010

Ваш первый вариант не будет работать, потому что .Take(10) преобразует его в IEnumerable<Log>. Ваш тип возврата - List<Log>, поэтому вам нужно будет сделать return logEntries.ToList().Take(10).ToList(), что более неэффективно.

Делая .ToList().Take(10), вы заставляете .Take(10) быть LINQ для объектов, в то время как другой способ передачи фильтра может быть передан в базу данных или другой базовый источник данных. Другими словами, если вы сначала выполните .ToList(), ВСЕ объекты должны быть перенесены из базы данных и размещены в памяти. ТОГДА вы фильтруете на первое 10. Если вы говорите о миллионах строк базы данных (и объектов), вы можете представить, как это ОЧЕНЬ неэффективно и не масштабируется.

Второй также будет запущен немедленно, потому что у вас есть .ToList(), поэтому нет никакой разницы.

2 голосов
/ 08 декабря 2010

Вторая версия будет более эффективной (как по времени, так и по использованию памяти). Например, представьте, что у вас есть последовательность, содержащая 1 000 000 элементов:

  1. Первая версия перебирает все 1 000 000 элементов, добавляя их в список по мере необходимости. Затем, наконец, он возьмет первые 10 элементов из этого большого списка.

  2. Второй версии нужно только перебрать первые 10 элементов, добавив их в список по мере необходимости. (Оставшиеся 999 990 предметов даже не нужно рассматривать.)

1 голос
/ 12 декабря 2011

Как насчет этого?

I have 5000 records in "items"

версия 1:

  IQueryable<T> items = Items; // my items
  items = ApplyFilteringCriteria(items, filter); // my filter BL 
  items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL
  items = items.Skip(0);
  items = items.Take(25);
  return items.ToList();

это заняло: 20 секунд на сервере

версия 2:

  IQueryable<T> items = Items; // my items
  items = ApplyFilteringCriteria(items, filter); // my filter BL 
  items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL
  List<T> x = items.ToList();
  items = x.Skip(0).ToList();
  items = x.Take(25).ToList();
  return x;

это заняло: 1 сек на сервере

Что вы думаете сейчас? Есть идеи почему?

0 голосов
/ 08 декабря 2010

Второй вариант.

Первый будет оценивать все перечисляемое, превращая его в List ();затем вы устанавливаете итератор, который будет перебирать первые десять объектов и затем выходить.

Второй устанавливает сначала итератор Take (), поэтому, что бы ни случилось позже, только 10 объектов будут оцениваться и отправлятьсяк обработке «вниз по течению» (в данном случае ToList (), который возьмет эти десять элементов и вернет их как конкретный список).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...