Expression Trees in LINQ Providers
See how providers translate expressions to queries.
Expression Trees in LINQ Providers is a free C# Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the C# Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why LINQ Needs Expression Trees
LINQ has two flavors. IEnumerable<T> runs lambdas in memory. IQueryable<T> receives them as expression trees so a provider can translate them into another language, like SQL.
IEnumerable vs IQueryable
The difference is in the parameter type: IEnumerable.Where takes Func<T,bool> (compiled), while IQueryable.Where takes Expression<Func<T,bool>> (a tree).
using System;
using System.Linq;
using System.Linq.Expressions;
int[] nums = { 1, 2, 3, 4 };
// IEnumerable path: lambda is a compiled delegate
var evens = nums.Where(n => n % 2 == 0);
Console.WriteLine(string.Join(",", evens)); // 2,4The Query Stays as Data
With IQueryable, your lambda is captured as an expression tree and is not executed yet. The provider inspects the tree to build a query.
using System;
using System.Linq;
using System.Linq.Expressions;
IQueryable<int> q = new[] { 1, 2, 3, 4, 5 }.AsQueryable();
var filtered = q.Where(n => n > 2);
Console.WriteLine(filtered.Expression.NodeType); // Call (a method-call tree)Inspecting a Query Expression
The Expression property on a queryable exposes the tree the provider will translate.
using System;
using System.Linq;
var q = new[] { 10, 20, 30 }.AsQueryable().Where(x => x >= 20);
Console.WriteLine(q.Expression.ToString().Contains("Where")); // TrueTranslation, Not Execution
A provider like Entity Framework walks the tree and emits SQL such as WHERE Age > 18. Your C# predicate never runs on the client; the database does the work.
The IQueryProvider Role
IQueryable holds an IQueryProvider. When you enumerate the query, the provider receives the expression tree and decides how to execute it.
using System;
using System.Linq;
var q = new[] { 1, 2, 3 }.AsQueryable();
Console.WriteLine(q.Provider.GetType().Name); // EnumerableQuery (the in-memory provider)Deferred Execution
The tree is only translated and run when you enumerate (e.g. ToList, foreach). Until then it is pure data you can keep composing.
using System;
using System.Linq;
var q = new[] { 1, 2, 3, 4 }.AsQueryable()
.Where(n => n > 1)
.Select(n => n * 10);
// nothing has run yet; now force it:
Console.WriteLine(string.Join(",", q.ToList())); // 20,30,40Composing Queries
Because each operator adds to the expression tree, you can build a query in stages and the provider sees the whole thing at execution time.
using System;
using System.Linq;
var baseQuery = new[] { 5, 15, 25, 35 }.AsQueryable();
var q = baseQuery.Where(n => n > 10);
q = q.Where(n => n < 30); // composed: 10 < n < 30
Console.WriteLine(string.Join(",", q.ToList())); // 15,25What Cannot Be Translated
Providers can only translate operations they understand. A predicate calling an arbitrary C# method may throw at execution because there is no SQL equivalent.
Writing Your Own Provider
Custom providers implement IQueryProvider and use an ExpressionVisitor to walk the tree and translate each node into their target query language.
Putting It Together
This shows the key insight: the same lambda is a delegate for IEnumerable but a tree for IQueryable, enabling translation.
using System;
using System.Linq;
using System.Linq.Expressions;
Expression<Func<int, bool>> predicate = n => n > 2;
var data = new[] { 1, 2, 3, 4 };
// IEnumerable needs a delegate:
Console.WriteLine(string.Join(",", data.Where(predicate.Compile()))); // 3,4
// IQueryable accepts the tree directly:
Console.WriteLine(string.Join(",", data.AsQueryable().Where(predicate))); // 3,4Quick Check
Confirm the role expression trees play in LINQ providers.
Recap
You learned how LINQ providers use expression trees.
IEnumerableuses compiledFunc;IQueryableusesExpression<Func>trees.- Providers translate the tree (e.g. to SQL) instead of running it.
- Execution is deferred until enumeration.
- Only translatable operations work; arbitrary methods may fail.
Frequently asked questions
Is the “Expression Trees in LINQ Providers” lesson free?
Yes — the full text of “Expression Trees in LINQ Providers” is free to read here on the web, and the C# Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the C# Academy course, upgrade to CoddyKit PRO.
What will I learn in “Expression Trees in LINQ Providers”?
See how providers translate expressions to queries. You practise C# Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start C# Academy?
No prior experience is required. C# Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Expression Trees in LINQ Providers” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this C# Academy lesson?
Yes. Every C# Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- What Are Expression Trees
- Building Expressions Manually
- Compiling and Executing Expressions
- Expression Trees in LINQ Providers