Friday, 05 September 2008
When writing our new messaging framework (GMX) for Genome v4, I ran into an interesting problem with LINQ. My colleague, Sztupi also ran into the same problem at almost the same time, so I thought it would make sense to write about it.

Before describing the problem, let me summarize some not-so-well-known facts about LINQ. If you are experienced with LINQ and the expression trees it uses, you can even skip this part and proceed from “So much for the LINQ overview” sentence to read about the problem I ran into.

When you write a query, such as

from c in customers
where c.City == "London"
select c
the C# compiler compiles it into a method call like this:

customers.Where(c => c.City == "London")

You can even write the Where() call directly as well; you don’t have to use the "from…" syntax. The parameter of the Where() call is a special construct called a lambda expression, which is something very similar to an anonymous method. In fact, sometimes it is an anonymous method.

Now the question is what you want to do with this lambda expression. If you want to filter customers that are already loaded into the memory, you want to have an anonymous method compiled from the lambda. However, if the customers reside in the database or in an XML file, you actually never want to evaluate the lambda as a .NET method call, but rather you want to transform it to SQL or XPath and let the underlying engine execute it. In this case, the anonymous method is not a good option, as it would be very hard to find out from the compiled CLR code that the method wanted to compare the City field to "London".

And here comes the big trick of LINQ. The C# compiler decides during compile time whether to compile the lambda expression to an anonymous method, or to an expression tree initialization code. If it compiles it to expression tree initialization, then during runtime, a new expression tree will be created whenever this Where() method is called, and this expression tree will represent the lambda expression you just described. O/RM engines like Genome can take this expression tree and transform it to SQL.

The only question remains is how the C# compiler can decide whether to compile the lambda to an anonymous method or to expression tree initialization. This decision is done by analyzing the parameter types of the Where() method you are actually about to call. If the Where() method takes a delegate as a parameter, it compiles to an anonymous method, and if it takes an Expression<T> parameter, it compiles to expression initialization.

It is good to know that the LambdaExpression class has a Compile() method, that can be used to compile the expression tree to a delegate. We don’t have a transformation in the other direction however, so you cannot get an expression tree from a delegate.

Genome | Linq
Friday, 05 September 2008 16:16:03 (W. Europe Daylight Time, UTC+02:00)  #    Disclaimer  |  Comments [0]  |