简而言之,我有一个接受的观点:
@model IEnumerable<MyModel>
Run Code Online (Sandbox Code Playgroud)
并继续遍历模型,使用以下方法创建表:
@for (int i = 0; i < Model.Count(); i++)
{
<tr id="tblRow_@(Model.ElementAt(i).Id)">
<td width="1">
@Html.DisplayFor(m => m.ElementAt(i).LoggedInUser)
</td>
<td width="1" class="date">
@Html.DisplayFor(m => m.ElementAt(i).DateCreated)
</td>
</tr>
}
Run Code Online (Sandbox Code Playgroud)
所以在控制器中我传递x给视图,它是:
var x = new DAL().GetList(); // returns IEnumerable<MyModel>
Run Code Online (Sandbox Code Playgroud)
或者
var x = new DAL().GetList().ToList();
Run Code Online (Sandbox Code Playgroud)
通过第一个(IEnumerable)比通过第二个(已经转换为列表)慢
为什么?
我假设它与 Model.Count() 有关系,也许它必须为每个周期将 the 转换IEnumerable为 a List,但即使只有 100 个条目,速度差异也会从几乎立即变为 8 秒。
TL;DR:foreach改为使用。请参阅底部的代码。
这主要与 ASP.NET 无关——它是 ASP.NET 的工作方式IEnumerable<T>及其扩展方法,特别是对于延迟创建的序列。
当你调用ToList()一个序列时,它List<T>通过询问原始序列的每个元素来创建一个- 但在那之后,你可以做任何事情(通过索引访问,获取计数等)而根本不咨询序列。它不仅不需要查询序列,而且 LINQ 还针对实现调用它们ElementAt()以及Count()何时调用它们进行了优化IList<T>。
如果你调用Count()一个IEnumerable<T>没有实现的IList<T>(或其他一些有帮助的接口),它必须从头到尾遍历整个序列。如果序列被惰性求值(例如使用带有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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这并不意味着您需要调用ToList()- 您可以重写整个循环以避免使用Count()和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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在棘手的部分是是否DisplayFor会做正确的事情。有可能它不会 - 我不HtmlHelper<T>知道到底发生了什么。您可能需要稍微更改代码才能使其正常工作。
如果您确实需要通过索引访问元素,我会将模型更改为 aList<T>并使用Count属性和常规索引器:
@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>
}
Run Code Online (Sandbox Code Playgroud)
这样你就没有隐藏的“也许它会很快,也许不会”依赖。