Tuesday, April 21, 2009

LINQ Expression tree

Lately I had to implement my own LINQ to SQL "converter" which uses a custom mapping stored in the database. It is even not as difficult as I thought. You have "only" to recursively parse the expression tree and convert it to the correcsponding SQL instructions.
The only thing I had some problems with were local variables or parameters.
Consider the following code:
int a = 3;
Get<Person>(p => p.Age == a);

the class Person
public class Person
{
public int Age { get; set; }
}

and the method
public static void Get<T>(Expression<Func<T, bool>> e)
{
MemberExpression member = (MemberExpression)((BinaryExpression)e.Body).Right;
ConstantExpression constant = (ConstantExpression)member.Expression;
Console.WriteLine(constant.Value.ToString());
}

when analyzing the expression tree then You see that the right expression of the BinaryExpression is of type ConstantExpression. Normally ConstantExpression contain directly a value. But this is not the case here! The console writeline produces the following: "ConsoleApplication1.Program+<>c__DisplayClass0"
The value of the constant expression is an anonymous object which is created on the fly. This object has a field which is named a and that field contains the value.
Therefore I had to do the following:
public static void Get<T>(Expression<Func<T, bool>> e)
{
MemberExpression member = (MemberExpression)((BinaryExpression)e.Body).Right;
ConstantExpression constant = (ConstantExpression)member.Expression;
FieldInfo info = (FieldInfo)member.Member;
Console.WriteLine(info.GetValue(constant.Value).ToString());
}

Instead of evaluating the expression which is associated to the MemberExpression.Expression I used the Member and casted it to a FieldInfo. Then I called the GetValue method passing it the value of the ConstantExpression which is basically the object. In this way I finally got the correct value of a.
The same works also for method parameters. Consider this code:
public static void FromMethod(int a)
{
Get<Person>(p => p.Age == a);
}

there we have exactly the same problem and it can be resolved in the way described above.

3 comments:

joni said...

Hi Manfred, I don't suppose you could post up the code you used for this? I am considering doing this myself in order to query with NHibernate using lambda expressions.

It actually seems odd to me that the internal methodology linq-to-sql uses to do this isn't available through some helper method.

Unknown said...

Hi Jonathan,
no problem, I will post some code for You in the next days :-)

Unknown said...

Hi Jonathan,
there is now a code sample online; check it out here http://manfred-ramoser.blogspot.com/2009/08/linq-expression-trees-and-ormapper.html