Expression Trees (`Expression<Func<T>>`) vs Compiled Delegates
1Concept
While `Func<T>` compiles to executable IL, `Expression<Func<T>>` compiles to an Abstract Syntax Tree (AST) data structure that ORMs (EF Core) can parse and translate into raw SQL.
2Architecture Diagram
Expression<Func<User, bool>> expr = u => u.Age > 18;
│
[ BinaryExpression (GreaterThan) ]
├── Left: [ MemberExpression (u.Age) ]
└── Right: [ ConstantExpression (18) ]
│
▼
Parsed by EF Core Provider ──> Emits: "SELECT * FROM Users WHERE Age > 18" 3Code Example
C# 13 & .NET 9
using System;
using System.Linq.Expressions;
public class ExpressionTreeDemo
{
public static void Main()
{
// Expression Tree (Data representation of code)
Expression<Func<int, bool>> isAdultExpr = age => age >= 18;
Console.WriteLine($"Node Type: {isAdultExpr.NodeType}");
Console.WriteLine($"Body: {isAdultExpr.Body}");
Console.WriteLine($"Parameters: {isAdultExpr.Parameters[0].Name}");
// Compile to runnable delegate at runtime
Func<int, bool> compiled = isAdultExpr.Compile();
Console.WriteLine($"Execution Result for Age 21: {compiled(21)}");
}
}4Expected Output
Node Type: Lambda Body: (age >= 18) Parameters: age Execution Result for Age 21: True
5Key Takeaways
- ✓Expression trees represent code as data inspectable at runtime.
- ✓`IQueryable<T>` operates on `Expression<Func<T>>` for server-side SQL translation.
- ✓Calling `.Compile()` at runtime is expensive; cache compiled delegates.