How to use ExpressionVisitor like a pro?

Recently I had to deal with ExpressionVisitor
class in C#. It is pretty easy concept as long as you understand ExpressionTrees. Basically, if you have ever created expression at runtime (equivalent of: Expression<Func<T, object>> expr = el => el.SomeProperty
, but this one is build at compile time), you will be able to handle ExpressionVisitor
class. It won't be the easiest experience ever, but definitely manageable.
What for it can be used? Definitely there are various way to utilize this functionality. The first one I can think of is when you want to create your own ORM as this is the way to translate this what you define in the code to SQL. Sometimes you may have to dynamically add a condition to an expression you pass to a method or you want to completely change it depending on some other, dynamic factors. That's the second usage I can think of now.
So what happens to our <expression> during compile time? How the compiler constructs the lambda in the code, so the runtime is able to execute it? Check the code below

Nice one, don't you think?
Imagine something more complex. Something like a filter
Expression<Func<T, bool>> expr = el => el.SomeProperty == "abc" && el.AnotherProperty >= 123

This is the amount of operations the compiler has to do when it strips down your lambda you created in the code.
Now, you basically know how expressions (or lambdas as this is the more popular name, even though Lambda
is only one type of Expression
) work and how they are constructed by the compiler. So what to do when you want to translate this to SQL?
ExpressionVisitor as a SQL translator
Below you can see a shortened version of a simple Translator that translates Lambda to SQL. If you want to see the full version of the file, use this link
public class LambdaToSqlTranslator : ExpressionVisitor
{
private StringBuilder _sqlAccumulator;
public string Translate(Expression expression)
{
this._sqlAccumulator = new StringBuilder();
this.Visit(expression);
return this._sqlAccumulator.ToString();
}
protected override Expression VisitBinary(BinaryExpression b)
{
_sqlAccumulator.Append("(");
this.Visit(b.Left);
switch (b.NodeType)
{
case ExpressionType.And:
case ExpressionType.AndAlso:
_sqlAccumulator.Append(" AND ");
break;
case ExpressionType.Or:
case ExpressionType.OrElse:
_sqlAccumulator.Append(" OR ");
break;
_sqlAccumulator.Append(" OR ");
break;
...
case ExpressionType.GreaterThan:
_sqlAccumulator.Append(" > ");
break;
case ExpressionType.GreaterThanOrEqual:
_sqlAccumulator.Append(" >= ");
break;
default:
throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", b.NodeType));
}
this.Visit(b.Right);
_sqlAccumulator.Append(")");
return b;
}
}
To use it, create the instance of an object and pass lambda. As a result you'll get a sql statement
var translator = new LambdaToSqlTranslator();
Expression<Func<SomeClass, bool>> whereQuery = el => el.SomeProperty != null && el.SomeProperty == "abc" && el.AnotherProperty >= 123;
var sql = translator.Translate(whereLambda);
When you dump sql
value, you'll see this:(((SomeProperty IS NOT NULL) AND (SomeProperty = 'abc')) AND (AnotherProperty >= 123))
Quite cool, isn't it? 🙂
ExpressionVisitor as a helper to change existing expressions
Now, imagine you have such a predicate expression which is passed next to sql through your layer which then executes ORM library
Expression<Func<SomeClass, bool>> expression = el => el.SomeProperty == "ABC" && el.AnotherProperty > 123
The new requirement for the software is to show only active entities and all inactive should be available only for other departments which have the database copied and they prepare reports for the business.
Obviously, you can go through the code base, find every single place which constructs lambda and add there
&& el.Active
It'll definitely work, but the amount of work required may be enormous, depending on the size of the application. With ExpressionVisitor
you can add (or remove/modify) such condition during runtime. Why not to change the existing expression? Because it is immutable! However, using with the visitor pattern, the expression tree is traversed from the top to the bottom and reconstructed again. This creates the opportunity to change the expression by adding additional clause to it.
If we define the following class:
public class ActiveOnlyExpressionVisitor : ExpressionVisitor
{
public override Expression Visit(Expression node)
{
var lambdaExpressionOfNode = (LambdaExpression)node;
var parameterExpression = (ParameterExpression)GetParameterExpression(node);
var activePropertyExpression = Expression.PropertyOrField(parameterExpression, nameof(SomeClass.Active));
var andExpression = Expression.And(lambdaExpressionOfNode.Body, Expression.Equal(activePropertyExpression, Expression.Constant(true)));
var newLambdaExpression = Expression.Lambda
return newLambdaExpression;
}
private static Expression GetParameterExpression(Expression expression)
{
if (expression.NodeType == ExpressionType.Lambda)
{
var lambdaExpression = (LambdaExpression)expression;
var parameterExpression = lambdaExpression.Parameters.FirstOrDefault(x => x.NodeType == ExpressionType.Parameter);
return parameterExpression;
}
return null;
}
}
and use it this way:
Expression<Func<SomeClass, bool>> expression = el => el.AnotherProperty == 123;
var newExpression = new ActiveOnlyExpressionVisitor().Visit(expression);
var sql = new LambdaToSqlTranslator().Translate(newExpression);
it'll produce the following sql statement:
((AnotherProperty = 123) AND (Active = 1))
Voila, you dynamically added Active = 1
to the lambda which was then translated to an appropriate SQL statement! Quite easy, don't you think? 🙂
ExpressionVisitor
together with Expression Trees
aren't so easy to understand, but the more you practice, the more you'll understand. To be honest, whenever I have to manipulate Expression Trees it makes me anxious, but I don't give up and just open LinqPad and I find the way to achieve what I want.
Happy coding! 🙂