渲染视图时是否会发生LINQ延迟执行,或者更早?

JK.*_*JK. 7 c# linq asp.net-mvc deferred-execution

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

在控制器中:

public ActionResult Index()
{
    var model = _fooService.GetAll();
    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

在模型中:

@foreach (var item in Model) {    
    <tr>
        <td>@item.Bar</td>
   </tr>
}
Run Code Online (Sandbox Code Playgroud)

当我们调用时,查询不会被执行_fooService.GetAll(),但是会延迟到稍后的某个时间点 - 但是它执行的确切位置是什么?

  • return View(model);控制器中的语句(看起来不像)?
  • @foreach (var item in Model)视图中的线?
  • 第一次@item.Bar看到视图中的线?
  • 介于两者之间的其他东西return View(model);和正在呈现的视图?

And*_*hov 5

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

更新 对于任何感兴趣的人,这里是"证据".创建一个这样的类:

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

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

@model PlayMvc.Models.DiagnosticCollection<string>
@{System.Diagnostics.Debug.Print("Before foreach");}
@foreach (var item in Model)
{
    System.Diagnostics.Debug.Print("After foreach");
    @item
}
Run Code Online (Sandbox Code Playgroud)


R0M*_*RMY 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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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


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

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

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