The Light
The Light

Reputation: 27011

How and When LINQ Queries are Translated and Evaluated?

I have a LINQ to SQL Query as below:

var carIds = from car in _db.Cars
           where car.Color == 'Blue'
           select car.Id;

The above will be translated into the below sql finally:

select Id from Cars 
where Color = 'Blue'

I've read that the phases are:

  1. LINQ to Lambda Expression
  2. Lambda Expression to Expression Trees
  3. Expression Trees to SQL Statements
  4. SQL Statements gets executed

So, my questions are when and how does this get translated and executed?

I know that phase 4 happens at runtime when my "carIds" variable get accessed within a foreach loop.

foreach(var carId in carIds) // here?
{
  Console.Writeline(carId) // or here?
}

What about the other phases? when do they occur? at compile time or runtime? at which line (on the definition or after the definition, when accessed or before it gets accessed)?

Upvotes: 7

Views: 427

Answers (2)

Marc Gravell
Marc Gravell

Reputation: 1062965

  1. is done by either you, the dev, or the C# compiler; you can write lambda expression manually, or with LINQ syntax they can be implied from LINQ (i.e. where x.Foo == 12 is treated as though it were .Where(x => x.Foo == 12), and then the compiler processes step 2 based on that)
  2. is done by the C# compiler; it translates the "lambda expression" into IL that constructs (or re-uses, where possible) an expression tree. You could also argue that the runtime is what processes this step
  3. and
  4. are typically done when the query is enumerated, specifically when foreach calls GetEnumerator() (and possibly even deferred to the first .MoveNext)

the deferred execution allows queries to be composed; for example:

var query = ...something complex...
var items = query.Take(20).ToList();

here the Take(20) is performed as part of the overall query, so you don't bring back everything from the server and then (at the caller) filter it to the first 20; only 20 rows are ever retrieved from the server.

You can also use compiled queries to restrict the query to just step 4 (or steps 3 and 4 in some cases where the query needs different TSQL depending on parameters). Or you can skip everything except 4 if you write just TSQL to start with.

You should also add a step "5": materialization; this is quite a complex and important step. In my experience, the materialization step can actually be the most significant in terms of overall performance (hence why we wrote "dapper").

Upvotes: 2

AdaTheDev
AdaTheDev

Reputation: 147264

What you are talking about is deferred execution - essentially, the linq to SQL query will be executed at the time you attempt to enumerate the results (e.g. iterate round it, .ToArray() it etc).

In your example, the statement is executed on the following line:

foreach(var carId in carIds)

See this MSDN article on LINQ and Deferred Execution

Upvotes: 3

Related Questions