Expression Trees
Remarks#
Expression trees are data structures used to represent code expressions in the .NET Framework. They can be generated by code and traversed programmatically to translate the code to another language or execute it. The most popular generator of Expression Trees is the C# compiler itself. The C# compiler can generate expression trees if a lambda expression is assigned to a variable of type Expression<Func<…>>. Usually this happens in the context of LINQ. The most popular consumer is Entity Framework’s LINQ provider. It consumes the expression trees given to Entity Framework and generates equivalent SQL code which is then executed against the database.
Simple Expression Tree Generated by the C# Compiler
Consider the following C# code
Expression<Func<int, int>> expression = a => a + 1;
Because the C# compiler sees that the lambda expression is assigned to an Expression
ParameterExpression parameterA = Expression.Parameter(typeof(int), "a");
var expression = (Expression<Func<int, int>>)Expression.Lambda(
Expression.Add(
parameterA,
Expression.Constant(1)),
parameterA);
The root of the tree is the lambda expression which contains a body and a list of parameters. The lambda has 1 parameter called “a”. The body is a single expression of CLR type BinaryExpression and NodeType of Add. This expression represents addition. It has two subexpressions denoted as Left and Right. Left is the ParameterExpression for the parameter “a” and Right is a ConstantExpression with the value 1.
The simplest usage of this expression is printing it:
Console.WriteLine(expression); //prints a => (a + 1)
Which prints the equivalent C# code.
The expression tree can be compiled into a C# delegate and executed by the CLR
Func<int, int> lambda = expression.Compile();
Console.WriteLine(lambda(2)); //prints 3
Usually expressions are translated to other languages like SQL, but can be also used to invoke private, protected and internal members of public or non-public types as alternative to Reflection.
building a predicate of form field == value
To build up an expression like _ => _.Field == "VALUE"
at runtime.
Given a predicate _ => _.Field
and a string value "VALUE"
, create an expression that tests whether or not the predicate is true.
The expression is suitable for:
IQueryable<T>
,IEnumerable<T>
to test the predicate.- entity framework or
Linq
toSQL
to create aWhere
clause that tests the predicate.
This method will build an appropriate Equal
expression that tests whether or not Field
equals "VALUE"
.
public static Expression<Func<T, bool>> BuildEqualPredicate<T>(
Expression<Func<T, string>> memberAccessor,
string term)
{
var toString = Expression.Convert(Expression.Constant(term), typeof(string));
Expression expression = Expression.Equal(memberAccessor.Body, toString);
var predicate = Expression.Lambda<Func<T, bool>>(
expression,
memberAccessor.Parameters);
return predicate;
}
The predicate can be used by including the predicate in a Where
extension method.
var predicate = PredicateExtensions.BuildEqualPredicate<Entity>(
_ => _.Field,
"VALUE");
var results = context.Entity.Where(predicate).ToList();
Expression for retrieving a static field
Having example type like this:
public TestClass
{
public static string StaticPublicField = "StaticPublicFieldValue";
}
We can retrieve value of StaticPublicField:
var fieldExpr = Expression.Field(null, typeof(TestClass), "StaticPublicField");
var labmda = Expression.Lambda<Func<string>>(fieldExpr);
It can be then i.e. compiled into a delegate for retrieving field value.
Func<string> retriever = lambda.Compile();
var fieldValue = retriever();
//fieldValue result is StaticPublicFieldValue
InvocationExpression Class
InvocationExpression class allows invocation of other lambda expressions that are parts of the same Expression tree.
You create them with static Expression.Invoke
method.
Problem We want to get on the items which have “car” in their description. We need to check it for null before searching for a string inside but we don’t want it to be called excessively, as the computation could be expensive.
using System;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
var elements = new[] {
new Element { Description = "car" },
new Element { Description = "cargo" },
new Element { Description = "wheel" },
new Element { Description = null },
new Element { Description = "Madagascar" },
};
var elementIsInterestingExpression = CreateSearchPredicate(
searchTerm: "car",
whereToSearch: (Element e) => e.Description);
Console.WriteLine(elementIsInterestingExpression.ToString());
var elementIsInteresting = elementIsInterestingExpression.Compile();
var interestingElements = elements.Where(elementIsInteresting);
foreach (var e in interestingElements)
{
Console.WriteLine(e.Description);
}
var countExpensiveComputations = 0;
Action incCount = () => countExpensiveComputations++;
elements
.Where(
CreateSearchPredicate(
"car",
(Element e) => ExpensivelyComputed(
e, incCount
)
).Compile()
)
.Count();
Console.WriteLine("Property extractor is called {0} times.", countExpensiveComputations);
}
private class Element
{
public string Description { get; set; }
}
private static string ExpensivelyComputed(Element source, Action count)
{
count();
return source.Description;
}
private static Expression<Func<T, bool>> CreateSearchPredicate<T>(
string searchTerm,
Expression<Func<T, string>> whereToSearch)
{
var extracted = Expression.Parameter(typeof(string), "extracted");
Expression<Func<string, bool>> coalesceNullCheckWithSearch =
Expression.Lambda<Func<string, bool>>(
Expression.AndAlso(
Expression.Not(
Expression.Call(typeof(string), "IsNullOrEmpty", null, extracted)
),
Expression.Call(extracted, "Contains", null, Expression.Constant(searchTerm))
),
extracted);
var elementParameter = Expression.Parameter(typeof(T), "element");
return Expression.Lambda<Func<T, bool>>(
Expression.Invoke(
coalesceNullCheckWithSearch,
Expression.Invoke(whereToSearch, elementParameter)
),
elementParameter
);
}
}
Output
element => Invoke(extracted => (Not(IsNullOrEmpty(extracted)) AndAlso extracted.Contains("car")), Invoke(e => e.Description, element))
car
cargo
Madagascar
Predicate is called 5 times.
First thing to note is how the actual propery access, wrapped in an Invoke:
Invoke(e => e.Description, element)
, and this is the only part that touches e.Description
, and in place of it, extracted
parameter of type string
is passed to the next one:
(Not(IsNullOrEmpty(extracted)) AndAlso extracted.Contains("car"))
Another important thing to note here is AndAlso
. It computes only the left part, if the first part returns ‘false’. It’s a common mistake to use the bitwise operator ‘And’ instead of it, which always computes both parts, and would fail with a NullReferenceException in this example.