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
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
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
horrible
@g I would be happy if you could give a valuable feedback on what was horrible. Thanks.
Thanks Chaks for taking time to write! Good work!