.NET Framework

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 type rather than a delegate type it generates an expression tree roughly equivalent to this code

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 to SQL to create a Where 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.


This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow