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).
Expression<TDelegate>, could be read as expression tree for delegates of type Func<T,bool>, is a tree-like data structure that describes the code of the TDelegate. This tree defines each node as an expression.
for example, given the following expression
Expression <Func <int, bool>> exp = n => n < 10;
its definition looks like this:
When do we need Expressions?
LINQ works for two kinds of data sources:
In-memory: data sources here are collections that are loaded in the memory and implement the IEnumerable interface or its derived interfaces. LINQ in this case uses its local query execution engine to do its job. We call this type LINQ-To-Objects
Remote: data sources, in this case, are external and they need providers that are capable to translate the LINQ query to an expressive query language in order to execute the query. These data sources should implement the IQueryable interface in order to be LINQ-enabled. We have a bunch of built-in providers, such as LINQ-To-SQL, LINQ-To-XML, LINQ-To-Entities, LINQ-To-DataSets, LINQ-To-CosmosDb. You can also find a large collection of third-party LINQ providers on NuGet.
Querying remote data sources requires the use of Expressions.
Defining Expression
LINQ default execution mode is the deferred mode. So the execution process will be done only when the result is the first time needed.
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:
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;
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:
Define Expression in C#
Expression<Func<Student, bool>> isTeenAgerExpr = s => s.Age > 12 && s.Age < 20;
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.
Define Expression in C#
Expression<Action<Student>> printStudentName = s => Console.WriteLine(s.StudentName);
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>.
Resource: Tutorialteacher, Medium
The Tech Platform
Comments