Given a very basic LINQ that is returned to a MVC view, at what exact point does the deferred execution fire?
In the controller:
public ActionResult Index()
{
var model = _fooService.GetAll();
return View(model);
}
In the model:
@foreach (var item in Model) {
<tr>
<td>@item.Bar</td>
</tr>
}
The query is not executed when we call _fooService.GetAll()
of course, but is deferred to some later point - but at which exact point is it executed?
return View(model);
statement in the controller (doesn't look like it)?@foreach (var item in Model)
line in the view?@item.Bar
line in the view is hit?return View(model);
and the view being rendered?Deferred execution means that the evaluation of an expression is delayed until its realized value is actually required. It greatly improves performance by avoiding unnecessary execution.
The basic difference between a Deferred execution vs Immediate execution is that Deferred execution of queries produce a sequence of values, whereas Immediate execution of queries return a singleton value and is executed immediately.
Benefits of Deferred Execution –It avoids unnecessary query execution and hence improves performance. Query construction and Query execution are decoupled, so we can create the LINQ query in several steps. A deferred execution query is reevaluated when you re-enumerate – hence we always get the latest data.
LINQ queries are always executed when the query variable is iterated over, not when the query variable is created. This is called deferred execution. You can also force a query to execute immediately, which is useful for caching query results. This is described later in this topic.
The query execution (I assume GetAll returns iqueryable) will be deferred into the view, and will be unwrapped on the foreach line, because that is the first place where you start iterating over the collection.
Update For anybody interested, here's "proof". Create a class like this:
public class DiagnosticCollection<T> : System.Collections.Generic.List<T>
{
public new Enumerator GetEnumerator()
{
Debug.Print("Collection Unwrap");
return base.GetEnumerator();
}
}
Test it in your view like this:
@model PlayMvc.Models.DiagnosticCollection<string>
@{System.Diagnostics.Debug.Print("Before foreach");}
@foreach (var item in Model)
{
System.Diagnostics.Debug.Print("After foreach");
@item
}
MSDN documentation addresses this question under the deferred query execution section (emphasis mine).
In a query that returns a sequence of values, the query variable itself never holds the query results and only stores the query commands. Execution of the query is deferred until the query variable is iterated over in a foreach or For Each loop...
That narrows down the answer to options 2 and 3.
foreach
is just syntactic sugar, underneath the compiler re-writes that as a while loop. There's a pretty thorough explanation of what happens here. Basically your loop will end up looking something like this
{
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();
}
}
Enumerator is advanced before it reaches your code inside the loop, so slightly before you get to @item.Bar
. That only leaves option 2, the @foreach (var item in Model)
line (though technically that line doesn't exist after the compiler is done with your code).
I'm not sue if the query will execute on the call to GetEnumerator()
or on the first call to e.MoveNext()
.
As @pst points out in the comments, there are other ways to trigger execution of a query, such as by calling ToList
, and it may not internally use a foreach
loop. MSDN documentation sort of addresses this here:
The IQueryable interface inherits the IEnumerable interface so that if it represents a query, the results of that query can be enumerated. Enumeration causes the expression tree associated with an IQueryable object to be executed. The definition of "executing an expression tree" is specific to a query provider. For example, it may involve translating the expression tree to an appropriate query language for the underlying data source. Queries that do not return enumerable results are executed when the Execute method is called.
My understanding of that is an attempt to enumerate the expression will cause it to execute (be it through a foreach
or some other way). How exactly that happens will depend on the implementation of the provider.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With