Expression in LINQ
We have learned that the lambda Expression can be assigned to the Func or Action type delegates to process over in-memory collections. The .NET compiler converts the lambda expression assigned to Func or Action type delegate into executable code at compile time.
LINQ introduced the new type called Expression that represents strongly typed lambda expression. It means lambda expression can also be assigned to Expression<TDelegate> type. The .NET compiler converts the lambda expression which is assigned to Expression<TDelegate> into an Expression tree instead of executable code. This expression tree is used by remote LINQ query providers as a data structure to build a runtime query out of it (such as LINQ-to-SQL, EntityFramework or any other LINQ query provider that implements IQueryable<T> interface).
The following figure illustrates differences when the lambda expression assigned to the Func or Action delegate and the Expression in LINQ.
We will learn Expression tree in the next section but first, let's see how to define and invoke an Expression.
Define an Expression
Take the reference of System.Linq.Expressions namespace and use an Expression<TDelegate> class to define an Expression. Expression<TDelegate> requires delegate type Func or Action.
For example, you can assign lambda expression to the isTeenAger variable of Func type delegate, as shown below:
Example: Define Func delegate for an expression in C#
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
}
Func<Student, bool> isTeenAger = s => s.Age > 12 && s.Age < 20;
Example: Define Func delegate for an expression in VB.Net
Dim isTeenAger As Func(Of Student, Boolean) = Function(s) s.Age > 12 And s.Age < 20
And now, you can convert the above Func type delegate into an Expression by wrapping Func delegate with Expresson, as below:
Example: Define Expression in C#
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.age > 12 && s.age < 20;
Example: Define Expression in VB.Net
Dim isTeenAgerExpr As Expression(Func(Of Student, Boolean)) =
Function(s) s.Age > 12 And s.Age < 20
in the same way, you can also wrap an Action<t> type delegate with Expression if you don't return a value from the delegate.
Example: Define Expression in C#
Expression<Action<Student>> printStudentName = s => Console.WriteLine(s.StudentName);
Example: Define Expression in VB.Net
Dim printStudentName As Expression(Action(Of Student) =
Function(s) Console.WriteLine(s.StudentName);
Thus, you can define Expression<TDelegate> type. Now, let's see how to invoke delegate wrapped by an Expression<TDelegate>.
Invoke an Expression
You can invoke the delegate wrapped by an Expression the same way as a delegate, but first you need to compile it using the Compile() method. Compile() returns delegateof Func or Action type so that you can invoke it like a delegate.
Example: Invoke Expression in C#
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.age > 12 && s.age < 20;
//compile Expression using Compile method to invoke it as Delegate
Func<Student, bool> isTeenAger = isTeenAgerExpr.Compile();
//Invoke
bool result = isTeenAger(new Student(){ StudentID = 1, StudentName = "Steve", Age = 20});
Example: Invoke Expression in VB.Net
Dim isTeenAgerExpr As Expression(Of Func(Of Student, Boolean)) =
Function(s) s.Age > 12 And s.Age < 20
'compile Expression using Compile method to invoke it as Delegate
Dim isTeenAger As Func(Of Student, Boolean) = isTeenAgerExpr.Compile()
Dim result = isTeenAger(New Student() With { .StudentID = 1, .StudentName = "Steve", .Age = 20})
Learn about the Expression tree in detail in the next section.
Why would you use Expression<Func<T>> rather than Func<T>?
I understand lambdas and the
Func
and Action
delegates. But expressions stump me. In what circumstances would you use an Expression<Func<T>>
rather than a plain old Func<T>
?
When you want to treat lambda expressions as expression trees and look inside them instead of executing them. For example, LINQ to SQL gets the expression and converts it to the equivalent SQL statement and submits it to server (rather than executing the lambda).
Conceptually,
Expression<Func<T>>
is completely different from Func<T>
. Func<T>
denotes a delegate
which is pretty much a pointer to a method and Expression<Func<T>>
denotes a tree data structure for a lambda expression. This tree structure describes what a lambda expression does rather than doing the actual thing. It basically holds data about the composition of expressions, variables, method calls, ... (for example it holds information such as this lambda is some constant + some parameter). You can use this description to convert it to an actual method (with Expression.Compile
) or do other stuff (like the LINQ to SQL example) with it. The act of treating lambdas as anonymous methods and expression trees is purely a compile time thing.Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
will effectively compile to an IL method that gets nothing and returns 10.
Expression<Func<int>> myExpression = () => 10;
will be converted to a data structure that describes an expression that gets no parameters and returns the value 10:
While they both look the same at compile time, what the compiler generates is totally different.
No comments:
Post a Comment