In part 1, we took a look at Expression Trees and how they fit into the LINQ stack.  Microsoft summarizes Expression Trees as:

"Expression trees represent language-level code in the form of data. The data is stored in a tree-shaped structure. Each node in the expression tree represents an expression, for example a method call or a binary operation such as x < y."

We also looked at how we can use the static methods in the Expression class to programmatically generate Expression Trees that can be ran against IQueryables.  Something similar to this:

[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 represents an often ignored way to leverage the power of LINQ in your applications.  However, the approach does come with a caveat.  Your code will produce unexpected results or even nasty run time errors if your Expression Tree is formed incorrectly.  And the problem with this is that bad Expression Trees are hard to debug.  The approach is simple, you want your generated Expression Tree to match the Expression Tree of its equivalent LINQ query.  In other words, you want the Expression Tree generated from the above code to match the Expression Tree of this query:

[code:c#]

NorthwindDataContext northwindContext = new NorthwindContext();

var q = from p in northwindContext.Products

where p.ProductID == 1

            select p;

Product p = q.Single();

[/code]

For example, if your hard-coded query's Expression Tree contains an Unary Expression but your generated Expression Tree doesn't then you'll be met with a nice run-time error (and the error message wont allude to what the real problem is).  Luckily, its trivial to obtain the Expression Tree of your sample query.

[code:c#]

Expression expr = q.Expression;

[/code]

I've found it useful to have a bit of code handy that prints the Expression Tree to a text file.  It would be pretty easy to add some additional features to it, maybe only print what is different between the two Trees.  But for now, I'm satisfied with comparing the Trees manually. Let's take a look at some of the code in the ExpressiveLog class.

[code:c#]

public void Process(Expression expr)
        {
            Type expressionType = expr.GetType();
            Print(Environment.NewLine + "Expression: " + expressionType.Name);
            PropertyInfo[] properties = expressionType.GetProperties();
            foreach(PropertyInfo prop in properties)
            {
                object propertyValue = prop.GetValue(expr, null);
                if (propertyValue != null)
                {
                    if (prop.PropertyType.GetInterfaces().Contains(typeof(ICollection)))
                    {
                        HandleCollection(prop, propertyValue);
                    }
                    else
                    {
                        Resolve(prop, propertyValue);
                    }
                }
                else
                {
                    Print(prop.Name + " : null");
                }
            }

            this.writer.Flush();
        }

[/code]

The code is pretty straight forward here.  I'm using Reflection to get the type of the Expression passed into the Process method.  I'm then printing the Expression type's name to the log file.  After that I need to get to get all of the properties of the Expression type.  I'm looping through the properties to see if it is a type that implements ICollection.  This is necessary because some Expressions contain ReadOnlyCollections that sometimes contain other Expression.  If its not a collection, I use the Resolve method to check if the property type is a Expression itself.

Let's look at the HandleConllection method:

[code:c#]

private void HandleCollection(PropertyInfo prop, object propertyValue)
        {
            ICollection collection = propertyValue as ICollection;
            foreach (object o in collection)
            {
                Resolve(prop, o);
            }
        }

[/code]

Pretty straight forward here as well. Cast the value of the property to ICollection.  Loop through the collection and call Resolve which checks to see if any of the objects in the collection is an Expression.

The Resolve method prints the value of the passed in object to the log file.  It then checks to see if that object is an Expression.  If it is then it starts the process over again by calling the Process method.  Otherwise, it checks to see if object is a MemberBinding type.

[code:c#]

private void HandleMemberBinding(object propertyValue)
{
    PropertyInfo[] properties = propertyValue.GetType().GetProperties();
    foreach (PropertyInfo property in properties)
    {
        object propValue = property.GetValue(propertyValue, null);
        Resolve(property, propValue);
    }
}

[/code]

 

MemberBindings are objects that usually contains Expressions.  We'll simply loop through its properties and call Resolve to handle Expressions that it comes across.

Here are the Print methods:

[code:c#]

private void Print(PropertyInfo prop, object instance) 
{
            Print(prop.Name + " : " + instance.ToString()); 
}

private void Print(string text) 
{
            this.writer.WriteLine(text); 
}

[/code]