I’m interested in the compilation and optimization LINQ expressions and whether or not I need to carefull consider the order of let and where clauses in my expression.
Here is example:
var query =
from record in Database.Table
let recordName = record.GetName()
let notUsed = UselessData()
let stuff = DoSomethingIntensiveWith(record)
where recordName == "foobar"
select stuff;
foreach (string item in query) {
Console.WriteLine("item => '{0}'", item);
}
Questions/assumptions:
record.GetName()must be resolved in order to execute thewhere
clause.notUsedis never used in the expression, so willUselessData()get called
at all?stuffis only needed ifrecordNameequals “foobar”. Will
DoSomethingIntensiveWith()be executed for every record or just records
whererecordNameequals “foobar”?
If I want to ensure DoSomethingIntensiveWith() is only called when
recordName equals “foobar”, do I need to position the let caluse after the
where clause, as follows:
var query =
from record in Database.Table
let recordName = record.GetName()
let notUsed = UselessData()
where recordName == "foobar"
let stuff = DoSomethingIntensiveWith(record)
select stuff;
foreach (string item in query) {
Console.WriteLine("item => '{0}'", item);
}
In the meantime, I’m going to play with some real code and the debugger. I’ll
report back what I find.
If this is LINQ-to-Objects, then: yes, you do. The standard
Enumerable.*implementation is very direct in terms of applying things in order. You don’t need all theletclauses necessarily, but things are still done in order, and that order is respected. If it is LINQ-to-anything-else, then all bets are off.This is easy to demonstrate:
The first query filters then projects (so it only does the expensive operation for the matching records), while the second query calculates the expensive operation for both:
It should be noted that
letis actually just implemented viaSelect– it is a projection from the source data to an anonymous type used internally to the query.