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

时间:2012-10-29 01:52:22

标签: c# asp.net-mvc linq deferred-execution

给定一个返回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 个答案:

答案 0 :(得分: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
}

答案 1 :(得分:3)

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

  

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

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

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

{
  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文档对此here

进行了解决
  

IQueryable接口继承IEnumerable接口,以便if   它表示一个查询,可以枚举该查询的结果。   枚举导致与IQueryable关联的表达式树   要执行的对象。“执行表达式”的定义   tree“特定于查询提供程序。例如,它可能涉及   将表达式树翻译成适当的查询语言   基础数据源。 不返回可枚举的查询   调用Execute方法时执行结果。

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

答案 2 :(得分:2)

作为澄清的一点,LINQ to SQL / Entity Framework在调用GetEnumerator时评估LINQ查询表达式。这在foreach的封面下使用,因此虽然延迟执行似乎等到foreach发生,但它实际上是foreach使用的GetEnumerator,它确实是关键的执行点。

答案 3 :(得分:-2)

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