In a previous blog post, we saw how we were able to build a powerful sorting interface that enhanced the OrderBy functionality found in LINQ. For those of us building HTTP APIs or web applications, it can make solving problems a joy. Hidden in the code for that solution is something called an ExpressionVisitor, and while the class may look unassuming, it provides a service that many may take for granted.

In this post, we’ll explore what an ExpressionVisitor is and how we can use it to inspect some of our Language Integrated Query (LINQ) expressions.

What Is An ExpressionVisitor

To understand what an ExpressionVisitor is, we need to know how LINQ works. Whenever we write a LINQ statement, the .NET framework converts it into what is called an Expression tree. This immutable structure contains different expression types depending on the complexity of our LINQ expression. Take the following expression:

v => (v + 1) / 2

Looking at the Expression<Funct<int,int>>, we can count the different unique expression types contained within the statement.

 Lambda: 1
 Divide: 1
 Add: 1
 Parameter: 2
 Constant: 2

Well, it is not as easy as looking at it, but that’s where an ExpressionVisitor can help.

An ExpressionVisitor traverses any expression tree, allowing us to view a current node or provide a new node in its place.

While visitors can provide new expressions as they traverse a tree, they cannot mutate an existing representation, since all expression trees are immutable. Let’s take a look at an ExpressionCounterVisitor that I wrote to help understand the make-up of any expression.

public class ExpressionCounterVisitor : ExpressionVisitor
{
    public Dictionary<string, int> Counts { get; private set; } = 
        new Dictionary<string, int>();

    public override Expression Visit(Expression node)
    {
        var key = node.NodeType.ToString();
        if (Counts.ContainsKey(key))
        {
            Counts[key] += 1;
        }
        else
        {
            Counts.Add(key, 1);
        }
        
        return base.Visit(node);
    }
}

In the ExpressionCounterVisitor we use the NodeType property as a key for our counting dictionary. As we visit each node, we add 1 to the total. This visitor is the most basic we could write, and every type of expression inherits from Expression, which allows us to catch every type.

All The Expression Types

There are many different types of Expressions, designed to reflect the variability in our expressions. There need to be enough to describe the robustness of the .NET languages it can represent. There are currently 85 expression types and some include: Block, Multiply, Negate, Or, and Parameter.

We can look at the ExpressionType enumeration to see all the other options.

Understanding The Expression Tree

In my previous blog post, we needed to understand whether we have applied an ordering operation to our expression. LINQ allows for subsequent ThenBy calls after an OrderBy call has been made. We wrote an ExpressionVisitor to look at our query.

private class OrderingMethodFinder : ExpressionVisitor
{
    public bool OrderingMethodFound { get; set; }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        var name = node.Method.Name;

        if (node.Method.DeclaringType == typeof(Queryable) && (
        name.StartsWith("OrderBy", StringComparison.Ordinal) ||
        name.StartsWith("ThenBy", StringComparison.Ordinal)))
        {
        OrderingMethodFound = true;
        }
        return base.VisitMethodCall(node);
    }
}

The OrderingMethodFinder triggers on every method call found in our expression. We do that by overriding the VisitMethodCall which passes in a MethodCallExpression. We can use this visitor like so:

var visitor = new OrderingMethodFinder();
visitor.Visit(queryable.Expression);

if (visitor.OrderingMethodFound)
{
    queryable = Direction == ListSortDirection.Ascending 
        ? ((IOrderedQueryable<TSource>)queryable).ThenBy(Filter) 
        : ((IOrderedQueryable<TSource>)queryable).ThenByDescending(Filter);
}
else
{
    queryable = Direction == ListSortDirection.Ascending 
        ? queryable.OrderBy(Filter) 
        : queryable.OrderByDescending(Filter);
}

Pretty cool right?!

Changing The Expression Tree

In addition to getting information about our expression, we can use a visitor to return a different node entirely. In the next example, we’ll use a visitor to replace all constants of 2 with another provided constant.

public class ReplaceTwosVisitor : ExpressionVisitor
{
    private readonly int constant;

    public ReplaceTwosVisitor(int constant)
    {
        this.constant = constant;
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        if (node.Value is int num && num == 2)
        {
            return Expression.Constant(constant);
        }

        return base.VisitConstant(node);
    }
}

Taking our original expression, we can replace the appropriate constants.

Expression<Func<int,int>> expression = v => (v + 1) / 2;
var mathIsHard = new ReplaceTwosVisitor(4);
var changed = (Expression<Func<int, int>>) mathIsHard.Visit(expression);
var function = changed.Compile();

var result = function(7); // 8/2 => 4
Console.WriteLine($"8/2 => {result}?!");

Running the above code gets us the “right” result.

8/2 => 2?!

What?! The result should have been 4, but with our sneaky visitor, we were able to change any constant of 2 => 4.

Conclusion

Expression trees are a necessity for delivering LINQ in .NET. Expressions aren’t magic, and with a little bit of research, we can do some fantastic stuff with the ExpressionVisitor class. We can get metadata about an expression to changing the expression entirely. For those needing to extend LINQ, the ExpressionVisitor is an essential tool in our toolbox.

I hope this post was eye-opening, and please leave a comment below if you have any other uses for ExpressionVisitor.