Skip to content

Expression trees C# 3.0new scenarios

Represent code as data structures for analysis or transformation.

For LINQ to be able to translate your queries into a different system at runtime it must be able to preserve the syntax of your operations rather than emitting code to perform it in-memory. Rather than trying to reverse-engineer compiled code at runtime the compiler instead preserves the intent of your code in an "expression tree".

When a function or variable expects a LambdaExpression - normally via the sub-type Expression<Func<T>> - the compiler parses the code as normal but rather than generating Intermediate Language (IL) instructions as normally it instead generates code that re-builds the code as an "expression tree".

This expression tree captures all the intent of the code you wrote in a way that LINQ providers can examine and then produce their own interpretation of (e.g. SQL) for remote execution (e.g. PostgreSQL).

Code

C#
// The compiler preserves the intent as a tree of expressions
Expression<Func<Customer, bool>> expr = c => c.City == "London";

// A LINQ provider can translate this to SQL, REST, etc.
var londonCustomers = db.Customers.Where(expr);
C#
// A compiled delegate — executable but opaque
Func<Customer, bool> func = c => c.City == "London";

// LINQ to Objects can execute this but no provider can translate it
var londonCustomers = customers.Where(func);

Notes

Expression trees:

  • can still be compiled and executed by using the Compile method on them
  • are immutable (they cannot be changed once built)
    • Expression Visitor lets you effectively substitute expressions with new expressions
    • these visitors are combined by nesting them at the top level
  • are not limited to LINQ - you can just take a function, examine it, modify it and compile it at runtime!

More information