Expression Trees C# Help

With LINQ to objects, the extension methods require a delegate type as parameter: this way, a Lambda expression can be assigned to the parameter. Lambda expressions can also be assigned to parameters of , type Expression<T>. The type’Expression<T> specifies that an expression tree made from the Lambda expression is stored in the assembly. This way the expression can be analyzed during runtime and optimized for doing the query to the data source.

Let’s turn to a query expression that was used previously:

var brazilRacers = from r in racers where r.Country == “Brazil”
orderby r.Wins select r;

This query expression is using the extension methods Where, OrderBy, and Select. The Enumerable class defines the Where () extension method with the delegate type Func<T, bool> as parameter predicate:

public static IEnumerable<TSource> Where<TSource> { this IEnumerable<TSource> source, Func<TSource, bool~ predicate);

This way, the Lambda expression is assigned to the predicate. Here, the Lambda expression is similar to an anonymous method, as was explained earlier:

Func<Racer, bool> predicate = r => r.Country == “Brazil”;

The Enumerable class is not the only class to define the Where () extension method. The Where ( ) extension method is also defined by the class Queryable<T>. This class has a different definition of the Where () extension-method:

public static IQueryable<Tsource> where<TSource>
this IQueryable<TSource> source, Expression<Func<TSource, bool» predicate);

Here, the Lambda expression is assigned to the type Expression<T>, which behaves differently:

Expression<Func<Racer, bool» predicata =
r => r.Country == ‘Brazil’;

Instead of using delegates, ‘the compiler emits an expression tree to the assembly. The expression tree can be read during runtime. Expression trees are built from classes that are derived from the abstract base class Expression. The Expression class is not the same as Expression<T>. Some of the expression classes that inherit from Expression are BinaryExpression, ConstantExpression,
InvodationExpression, LarnbdaExpression, NewExpression, NewArrayExpression, TernaryExpression, UnaryExpression, and so on. The compiler creates an expression tree resulting from the Lambda expression.

For example.the Lambda expression r. country == ‘Brazil’ makes use of ParameterExpression, MemberExpression, ConstantExpression, and MethodCallExpression to create a tree and store the tree in the assembly. This tree is then used during runtime to create an optimized query to the underlying data source.

The method DisplayTree () is implemented to display ail expression tree graphically on the console. Here an Expression object can be passed, and depending on the expression type some information about the expression is written to the console. Depending on the type of the expression, DisplayTree ( ) is called recursively.

With this method not all expression types are dealt with; only the types that are used with the next sample expression.

private static void DisplayTree(int indent, string message, Expression expression) string output = String.Format(·{O} {l}’ +.
‘! NodeType: {2}; Expr: {3} ” ··.PadLeft(indent, ‘>’), message,
expression.NodeType, expression); . indent++;
switch (expression.NodeType)
{
case ExpressionType.Lambda: Console.WriteLine(output);
LambdaExpression lambdaExpr (LambdaExpression) expression;
foreach (var parameter in lambdaExpr.Parameters)
DisplayTree (indent , •Parameter’ , . parameter) ;
}
DisplayTree(indent, ‘Body’, lambdaExpr.Body);
break;

case ExpressionType.Constant: constantExpression constExpr
(ConstantExpression) expression; console.writeLine(‘{O} Const Value: ‘ + ‘{l}’, output, constExpr.Value);
break;
case ExpressionType.Parameter: parameterixpression paramExpr
(ParameterExpression) expression; Conso le ,wri teLine (,{O} Param Type: .(1} ,, output, paramExpr.Type.Name);
break;
case ExpressionType.Equal: case ExpressionType.AndAlso:
case ExpressionType.GreaterThan: BinaryExpression binExpr =
(BinaryExpression) expression; if (binExpr.Method != null)
{
‘,.’i
Console.WriteLine(‘{O) Method: {l}’, output, binExpr.Method.Name);
-. ,.. Console.WriteLine(output)i
}
DisplayTree (.’ient, ‘Left’, binExpr.Leit),
DisplayTree(indent, ‘Right’, binExpr .Right) ;
break;
case ExpressionType.MemberAccess: MemberExpression memberExpr = (MemberExpression) expression;
Console.WriteLine(‘{O} Member Name: ‘ + ‘(l}, Type: {2}’, output,
memberExpr .Member .Name , memberExpr .Type. Name) ;
DisplayTree(indent, ‘Member Expr’, memberExpr.txpression);
break;
default:
Console.WriteLine();
Console. Wri teLine (‘…. {O} {l}’,
expression.NodeType,
expression.Type.Name);
break;

}

}

The expression that is used for showing the is already well known.It’s a Lambda expression with a Racer parameter, and the body of the expression takes racers from Brazil only if they have won more than six races:

Expression<Func<Racer, bool» expression r => r.Country == “Brazil” && r.wins > 6; DisplayTree(O, “Lambda”, expression);

Let’s look at the tree result. As you can see from the output, the Lambda expression consists of a Parameter and an And Also node type. The And Also node type has an Equal node type to the left and a Greater Than node type to the right. The Equal node type to the left of the And Also node type has a Member Access node type to the left and a Constant node type to  the right, and so on.

Lambda! NodeType: Lambda; Expr: r => ((r.Country.= “Brazil”) && (r.Wins > 6)) > Parameter! NodeType: Parameter; Expr: r Param Type: Racer > Body! NodeType: AndAlso; Expr: ((r.Country = “Brazil”) && (r.Wins > 6)) » Left! NodeType: Equal; Expr: (r.Country = “Brazil”) Method: op_Equality »> Left! NodeType: MemberAccess; Expr: r.Country Member Name: Country, Type: String »» Member Expr! NodeType: Parameter; Expr: r Param Type: Racer »> Right! NodeType: Constant; Expr: “Brazil” Const Value: Brazil » Right! NodeType: GreaterThan; Expr: (r.Wins > 6) »> Left! NodeType: MemberAccess; Expr: r.Wins Member Name: Wins, Type: Int32 »» Member Expr! NodeType: Parameter; Expr: r Param Type: Racer »> Right! NodeType: Constant; Expr: 6 Const Value: 6

One example where the Expression<T> type is used is with LINQ to SQL. LINQ to SQL defines extension methods with Exptession<T> parameters. This way the LINQ provider accessing the database can create a runtime-optimized query by reading the expressions to get the data from the database .

Posted on October 30, 2015 in Language Integrated Query

Share the Story

Back to Top