7

给定一个返回到 MVC 视图的非常基本的 LINQ,延迟执行在什么时候触发?

在控制器中:

public ActionResult Index()
{
    var model = _fooService.GetAll();
    return View(model);
}

在模型中:

@foreach (var item in Model) {    
    <tr>
        <td>@item.Bar</td>
   </tr>
}

当然,当我们调用时,查询不会执行_fooService.GetAll(),而是推迟到稍后的某个时间点——但它在哪个确切时间点执行?

  • 控制器中的return View(model);语句(看起来不像)?
  • 视图中的@foreach (var item in Model)线?
  • 第一次@item.Bar点击视图中的线?
  • return View(model);在与正在渲染的视图之间发生的其他事情?
4

4 回答 4

5

查询执行(我假设 GetAll 返回 iqueryable)将推迟到视图中,并将在 foreach 行上展开,因为这是您开始迭代集合的第一个地方。

更新 对于任何感兴趣的人,这里是“证明”。像这样创建一个类:

public class DiagnosticCollection<T> : System.Collections.Generic.List<T>
{
    public new Enumerator GetEnumerator()
    {
        Debug.Print("Collection Unwrap");
        return base.GetEnumerator();
    }
}

像这样在你的视图中测试它:

@model PlayMvc.Models.DiagnosticCollection<string>
@{System.Diagnostics.Debug.Print("Before foreach");}
@foreach (var item in Model)
{
    System.Diagnostics.Debug.Print("After foreach");
    @item
}
于 2012-10-29T02:09:37.040 回答
3

MSDN 文档在延迟查询执行部分(强调我的)下解决了这个问题。

在返回值序列的查询中,查询变量本身从不保存查询结果,只存储查询命令。查询的执行被推迟,直到查询变量在 foreach 或 For Each 循环中被迭代...

这缩小了选项 2 和 3 的答案。

foreach只是语法糖,在编译器下面将其重写为while循环。对这里发生的事情有一个非常彻底的解释。基本上你的循环最终会看起来像这样

{
  IEnumerator<?> e = ((IEnumerable<?>)Model).GetEnumerator();
  try
  { 
    int m;  // this is inside the loop in C# 5
    while(e.MoveNext())
    {
      m = (?)e.Current;
      // your code goes here
    }
  }
  finally
  { 
    if (e != null) ((IDisposable)e).Dispose();
  }
}

枚举器在到达循环内的代码之前是高级的,所以在你到达@item.Bar. 这只剩下选项 2,即该@foreach (var item in Model)行(尽管从技术上讲,在编译器完成您的代码后该行不存在)。

GetEnumerator()如果查询将在调用或第一次调用时执行,我不起诉e.MoveNext()


正如@pst 在评论中指出的那样,还有其他方法可以触发查询的执行,例如通过调用ToList,并且它可能不会在内部使用foreach循环。MSDN 文档在这里解决了这个问题

IQueryable 接口继承了 IEnumerable 接口,因此如果它表示一个查询,则可以枚举该查询的结果。 枚举导致与要执行的 IQueryable 对象关联的表达式树。“执行表达式树”的定义特定于查询提供程序。例如,它可能涉及将表达式树转换为底层数据源的适当查询语言。调用 Execute 方法时会执行不返回可枚举结果的查询。

我对此的理解是尝试枚举表达式将导致它执行(无论是通过foreach某种方式还是其他方式)。具体如何发生将取决于提供者的实现。

于 2012-10-29T04:25:42.360 回答
2

作为澄清点,LINQ to SQL/Entity Framework 在调用 GetEnumerator 时评估 LINQ 查询表达式。这被 foreach 在幕后使用,因此虽然延迟执行似乎要等到 foreach 发生,但实际上 foreach 使用的 GetEnumerator 才是真正的关键执行点。

于 2012-10-29T15:46:46.023 回答
-2

工作被推迟到需要完成时。在您的示例中,查询将在尝试获取item.

于 2012-10-29T04:05:56.647 回答