C# 3.0 Language Enhancements for LINQ – Part 3 – Expression Trees

14 04 2008

We saw more about Lambda Expression in our Part 1 of this series .Wonder how those were compiled or how those expressions parsed?

To do so, we need to get into Expression Trees!

So what is an Expression Tree?

Expression trees are nothing but those which represent the query itself.

Let us take a simple example.

   1: Func<string, string> function = 
   2:              x => x.Contains("c");

· The compiler constructs a delegate which takes an input parameter of type string and returns a value of type string

But, how about this

   1: Expression<Func<string, string>> expression = 
   2:                             x => x.Contains("c");

· The compiler generates an Expression Tree which we can parse and do something with the data

(This post gives a small introduction to Expression Trees. Expression Trees itself is a big topic but this post will help us go through some of the basics with a simple example)

Say we have a class called Student which has only one property – Name

   1: public class Student
   2: {
   3:     private string name;
   4:  
   5:     public string Name
   6:     {
   7:         get { return name; }
   8:         set { name = value; }
   9:     }
  10: }

And now we have a small function which is going to take an expression as its parameter and return the value for which the expression is queried for.

   1: public static string 
   2:          GetName(Expression<Func<Customer, bool>> predicate)

So, if we pass an expression,

n=>n.Name==”Chakkaradeep”

We need to get back Chakkaradeep , as that’s what we queried. This isn’t a good example, but I would say its good to understand how Expressions are parsed J

Let me show you the function directly now,

   1: public static string GetName(Expression<Func<Student, bool>> predicate)
   2: {
   3:     BinaryExpression binaryExpr = (BinaryExpression)predicate.Body;
   4:  
   5:     MemberExpression leftExpr = (MemberExpression)binaryExpr.Left;
   6:  
   7:     string url = leftExpr.Member.Name;
   8:  
   9:     if (binaryExpr.NodeType == ExpressionType.Equal)
  10:     {
  11:         url += "==";
  12:     }
  13:     else
  14:         throw new NotSupportedException("only = is supported");
  15:  
  16:     ConstantExpression rightValue = (ConstantExpression)binaryExpr.Right;
  17:  
  18:     url += rightValue.Value;
  19:  
  20:     return url;
  21: }

That’s a bit confusing! Whats happening ?

Expression is being parsed 🙂

Yes, take our expression again,

n=>n.Name==”Chakkaradeep”

Expressions always a Body property. In the above expression, we have a BinaryExpression

n.Name==”Chakkaradeep”

As you might have guessed by now, a BinaryExpression is something that has a binary operator

So lets take the Body and break it into two pieces

image

   1: BinaryExpression binaryExpr = 
   2:       (BinaryExpression)predicate.Body;

Now we have extracted our BinaryExpression body which has a Left property and Right property, as shown above in the diagram.

The Left property will hold n.Name

The Right Property will hold Chakkaradeep

And our Node Type or the Binary operator is == (Equality)

And that’s what we have done,

   1: string url = leftExpr.Member.Name;
   2:  
   3: if (binaryExpr.NodeType == ExpressionType.Equal)
   4: {
   5:     url += "==";
   6: }
   7: else
   8:     throw new NotSupportedException("only = is supported");
   9:  
  10: ConstantExpression rightValue = (ConstantExpression)binaryExpr.Left;
  11:  
  12: url += rightValue.Value;

As now you have the values, you could do anything with them and parse them 🙂

But yes, it would be more complicated if we have multiple queries coming up, like,

n=>n.Name==”Chakkaradeep” && n.Name==”Chaks”

And our sample will not work for the above query as we havent dealt with it. I leave to the reader to explore more on how to recursively parse Expressions 🙂

What if we need to manually build Expressions?

You can 🙂

We just use the technique of how we parsed to build the expression manually.

First, we need to create a ParameterExpression of type Student and also tell that our paramter variable is x

   1: //construct the parameter which is x and it is of type Student
   2: ParameterExpression xParam = 
   3:   Expression.Parameter(typeof(Student), "x");

And now we need to build our BinaryExpression whose Left Property is a MemberExpression which has the Property Name and the Right Property a value

   1: //construct our MemberExpression whose
   2: //left property is x.Name, and
   3: //right property’s value "Chakkaradeep"
   4: Student student = new Student();
   5: student.Name = "Chakkaradeep";
   6: MemberExpression leftExpr = MemberExpression.Property(xParam, "Name");
   7: Expression rightExpr = Expression.Constant(student.Name);
   8: //construct our BinaryExpression which is x.Name=="Chakkaradeep"
   9: BinaryExpression myExpr = MemberExpression.Equal(leftExpr, rightExpr);

All good now to build our Expression. Remember, we haven’t yet built our Expression, but we do have the bits and pieces that we need to build our expression 

image

And now we build our Expression,

   1: //now build our Expression using the parameter and BinaryExpression
   2: //x=>x.Name=="Chakkaradeep"
   3: Expression<Func<Student, bool>> lambdaExpr =
   4:     Expression.Lambda<Func<Student, bool>>
   5:         (
   6:             myExpr,
   7:             new ParameterExpression[] { xParam }
   8:         );

There you go! We build it telling that we have a ParameterExpression of type Student and the parameter is x which is associated with the BinaryExpression

And now you can directly pass this lambdaExpr to our function

GetName(lambdaExpr);

You can download the sample here

Useful Resources

1) Expression Trees

2) Expression Tree Visitor

3) System.Linq.Expressions Namespace

Advertisements

Actions

Information

3 responses

15 05 2008
g

horrible

15 05 2008
Chaks

@g I would be happy if you could give a valuable feedback on what was horrible. Thanks.

9 09 2008
Sat

Thanks Chaks for taking time to write! Good work!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: