If you're at all familiar with LINQ internals, then you probably have at least a basic understanding of what Expression Trees are and their importance in the LINQ stack. For the uninitiated, LINQ queries (Lambdas) come in two forms: Expression Trees and Delegates. Delegates are just anonymous methods. LINQ Expression Trees are the object oriented representation of a particular LINQ query.
Expression Trees are the building blocks of the LINQ stack. Programmers are able to apply integrated query to any problem domain by traversing a Lambda's Expression Tree. Want to use LINQ query syntax to query objects? Or XML? or Databases? Or even Amazon Web Services? It's all made possible by parsing the Expression Tree of a lambda and much of the discussion about programming with Expression Trees has centered around creating custom LINQ Providers in this manner. However, there is another powerful usage of Expression Trees which involves programmatically building them from the ground up. This approach has some pretty worth-while applications including automatic CRUD operations with LINQ to SQL.
An example of generating a CRUD operation by building Expression Trees:
[code:c#]
Expression paramExpr = Expression.Parameter(typeof(Product), "p");
Expression leftSide = Expression.Property(param, "ProductID");
Expression rightSide = Expression.Constant(1, typeof(int));
Expression equalExpr = Expression.Equal(leftSide, rightSide);
Expression lambda= Expression.Lambda(equalExpr, paramExpr);
NorthwindDataContext northwindContext = new NorthwindDataContext();
IQueryable<Product> queryable = northwindContext.Products;
Expression callExpr = Expression.Call(typeof(Queryable), "Where", new Type[] {
typeof(Product)}, Expression.Constant(queryable), pred);
IQueryable<Product> result = queryable.AsQueryable().Provider.CreateQuery<Product>(callExpr);
Product p = result.Single();
[/code]
The above code is equivalent to:
[code:c#]
NorthwindDataContext northwindContext = new NorthwindContext();
Product p = northwindContext.Products.Where(i => i.ProductID == 1).Single();
[/code]
or
[code:c#]
NorthwindDataContext northwindContext = new NorthwindContext();
var q = from p in northwindContext.Products
where p.ProductID == 1
select p;
Product p = q.Single().
[/code]
As you begin to build more complicated Expression Trees to represent more complex queries, you'll notice that any slight anomaly in your generated Expression Tree will result in bad queries. A helpful strategy for debugging these issues is to write a sample LINQ query that returns your expected results and compare that to your own generate queries. This can easily done with a little reflection and recursion as we'll see in Part 2.