Slow foreach() в запросе LINQ - ToList() значительно повышает производительность - почему?
Я как бы понимаю всю концепцию замедленного исполнения, но следующее меня озадачивает...
В DataTable, содержащем около 1000 строк, я вызываю AsEnumerable(). Затем я выбираю объекты, возвращаемые в IEnumerable строго типизированных классов (1)... Здесь, где я запутываюсь: я делаю цикл foreach в коллекции; выбирая материал из отдельных элементов в коллекции, используя кучу Where() calls (2)... И он мертв медленно.
-
DataTable.AsEnumerable().Select(r => new ObjectRepresentation { ... });
-
item.Where(i => i.SomeEnum == SomeEnum.Something)
... Но если я вызову ToList() сразу после моего вызова AsEnumerable() в DataTable, цикл foreach займет менее секунды.
Что мне здесь не хватает? Действительно ли я вызываю AsEnumerable() каждый раз, когда цикл повторяется? Или каждый раз, когда я получаю доступ к элементу коллекции? Или каждый раз, когда я делаю вызов Where() для элемента в коллекции? Или все вышеперечисленное?
Обновление
Несколько полный код:
public class ObjectRepresentation
{
public SomeEnum SomeEnum { get; set; }
}
var collection = DataTable.AsEnumerable().Select(r => new ObjectRepresentation
{
SomeEnum = (SomeEnum)Convert.ToInt32(r["SomeEnum"])
});
foreach(var item in collection) // slow loop
{
// 10 or so Where() calls on item inside this loop
}
collection = collection.ToList(); // Hit hyper speed button!
foreach(var item in collection) // fast loop
{
// 10 or so Where() calls on item inside this loop
}
Ответы
Ответ 1
Он не получит все элементы из базы данных, пока вы не наберете
ToList or First or Single
В foreach вы отправляете запрос в базу данных для каждого элемента. Таким образом, он работает медленнее. Откройте ваш профилировщик sql, чтобы лучше понять.
Ответ 2
Вы не понимаете, какие методы отложены, а какие нет, поэтому вы не понимаете, когда ваш код определяет операции и выполняет операции.
Все они отложены. Они определяют, но не выполняют операцию.
source.AsEnumerable
source.Select
source.Where
Они перечисляют источник и поэтому не откладываются.
source.ToList
source.First
source.Single
foreach(var x in source)
Ответ 3
Действительно, вы, похоже, не имеете четкого представления о том, что такое выполнение кода, и каково определение намерения (возможно) выполнить позже, когда результаты фактически используются. Я предлагаю прочитать эту часть LINQ.
И, возможно, попробуйте выполнить оба ваших варианта с прикрепленным отладчиком, чтобы вы действительно могли видеть, какой код выполняется в каком порядке и что на самом деле происходит с вашими данными. У вас может быть сюрприз (большой?) Здесь...