TL; DR: используйте foreach
вместо. См. Код внизу.
Это вообще не совсем ASP. NET - это способ, которым IEnumerable<T>
и методы расширения на нем работают, в частности в отношении лениво созданных последовательностей.
Когда вы вызываете ToList()
для последовательности, это создает List<T>
, запрашивая каждый элемент исходной последовательности - но после этого вы можете делать все (доступ по индексу, подсчет и т. д. c), не обращаясь к последовательности вообще. Мало того, что не нужно обращаться к последовательности, но LINQ имеет оптимизации для ElementAt()
и Count()
, когда они вызываются на IList<T>
реализации.
Если вы вызываете Count()
на IEnumerable<T>
, который не реализует IList<T>
(или любой из нескольких других полезных интерфейсов), он должен выполнить l oop через всю последовательность из начало, пока не дойдет до конца. Если последовательность вычисляется лениво (например, с использованием блока итератора с операторами yield
, это означает, что нужно снова выполнять работу.
ElementAt()
аналогично, за исключением того, что не нужно добираться до самого конца - оно просто нужно добраться до указанного элемента.
Вот полное консольное приложение, которое достаточно ясно демонстрирует проблему - запустите его внимательно и убедитесь, что вы понимаете вывод:
using System;
using System.Collections.Generic;
using System.Linq;
class Test
{
static void Main()
{
var sequence = CreateSequence();
ConsumeList(sequence);
ConsumeSequence(sequence);
}
static void ConsumeList(IEnumerable<int> sequence)
{
Console.WriteLine("Start of ConsumeList");
var list = sequence.ToList();
Console.WriteLine("ToList has completed - iterating");
for (int i = 0; i < list.Count(); i++)
{
var element = list.ElementAt(i);
Console.WriteLine($"Element {i} is {element}");
}
Console.WriteLine("End of ConsumeList");
Console.WriteLine();
}
static void ConsumeSequence(IEnumerable<int> sequence)
{
Console.WriteLine("Start of ConsumeSequence");
var list = sequence.ToList();
for (int i = 0; i < sequence.Count(); i++)
{
var element = sequence.ElementAt(i);
Console.WriteLine($"Element {i} is {element}");
}
Console.WriteLine("End of ConsumeSequence");
}
static IEnumerable<int> CreateSequence()
{
for (int i = 0; i < 5; i++)
{
var value = i * 2;
Console.WriteLine($"Yielding {value}");
yield return value;
}
}
}
Это не означает, что вам нужно звонить ToList()
, хотя - весь ваш l oop можно переписать, чтобы избежать использования Count()
и ElementAt
полностью:
@foreach (var element in Model)
{
<tr id="tblRow_@(element.Id)">
<td width="1">
@Html.DisplayFor(m => element.LoggedInUser)
</td>
<td width="1" class="date">
@Html.DisplayFor(m => element.DateCreated)
</td>
</tr>
}
Теперь есть сложная часть DisplayFor
поступит правильно. возможно , что не получится - я не знаю достаточно о HtmlHelper<T>
, чтобы точно знать, что там происходит. Возможно, вам придется изменить код немного больше, чтобы это работало.
Если вам нужно нужно получить доступ к элементу по индексу, я бы изменил модель на List<T>
и использовал бы свойство Count
и обычный индексатор вместо:
@for (int i = 0; i < Model.Count; i++)
{
<tr id="tblRow_@(Model[i].Id)">
<td width="1">
@Html.DisplayFor(m => m[i].LoggedInUser)
</td>
<td width="1" class="date">
@Html.DisplayFor(m => m[i].DateCreated)
</td>
</tr>
}
* 10 57 * Таким образом, у вас не будет скрытой зависимости «может быть, она будет быстрой, а может и не будет».