The other day, I was tasked with the problem of selecting entities from IQueryable<T> based on specific filter criteria that were only known at run-time. Basically, I had objects of different type with a set of properties.

Here is an example, of what we were trying to solve. We have two expressions that apply to different types and both return bool.

Expression<Func<SomeClassToBeFiltered, bool>> entityExpression = e => e.Property1 >= 42;
Expression<Func<MagicFilter, bool>> filterExpression = f => f.IsPresent && f.ItemsAvailable > 0;

The goal is to combine these two, but obviously this will not compile (for several reasons):

var expr = entityExpression && filterExpression;

Let’s have a look how these classes would be have to be defined, so we can solve this. First, we need an interface to indicate, when we encounter a filterable entity:

public interface ICanBeFiltered { }

public class SomeClassToBeFiltered : ICanBeFiltered
{

public int Property1 { get; set; }
}

The filter would look then like this:

public interface IAmAFilter { }

public class MagicFilter : IAmAFilter
{

public MagicFilter(ICanBeFiltered entity) { /* ... */ }

public bool IsPresent { get; set; }
public int ItemsAvailable { get; set; }
}

Note, that we define a constructor on the filter, that accepts an entity of type ICanBeFiltered. So in pseudo-code we could then do something like this:

e => e.Property1 >= 42 && e => (var f = new MagicFilter(e)).(f => IsPresent && f.ItemsAvailable > 0)

ExpressionVisitor to the rescue! We need to rewrite the LambdaExpression filterExpression

    private class RewriteFilterTypeExpression<TFilterable, TFilterType> : ExpressionVisitor
        where TFilterable : ICanBeFiltered
        where TFilterType : IAmAFilter
    { }

… override VisitParameter

protected override Expression VisitParameter(ParameterExpression node) =>
ReferenceEquals(replacing, node)

? replacement
: base.VisitParameter(node);

… and override VisitLambda

protected override Expression VisitLambda(Expression node)

… and inside it define the creation of a new instance of the filter …

var newFilterType = Expression.New
(

_filterTypeCtor,
replacement
);

… then we declare a variable to hold the instance of the filter …

var filterInstance = Expression.Variable(typeof(TFilterType), "filterTypeInstance");

… replace the inner filter parameter with the newly created filter instance parameter …

var visitor = new RewriteFilterTypeExpression(node.Parameters.Single(), filterInstance);
var replaced = visitor.Visit(node) as LambdaExpression;

… and then finally create a BlockExpression to wrap this together and return the result of the original expression …

return Expression.Block
(
new[] { filterInstance },
Expression.Assign(filterInstance, newFilterType),
Expression.Condition
(
replaced.Body,
Expression.Constant(true),
Expression.Constant(false)
)
);

The expression block can now be logically combined with the first expression, as now both expect the same input type. Then the resulting expression would look similar to this:

Lambda1(Com.Example.SomeClassToBeFiltered $e)
{
$e.Property1 >= 42 && .Block(Com.Example.MagicFilter $filterTypeInstance)
{
$filterTypeInstance = .New Com.Example.MagicFilter($e);
.If (
(System.Boolean)$filterTypeInstance.IsPresent == True &&

(System.Boolean)$filterTypeInstance.ItemsAvailable > 0
) {
True
} .Else {
False
}
}
}

Once again, with the power of expressions, it is possible to achieve the seemingly impossible …

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.