Search code examples
c#entity-frameworklinqexpression-trees

Generating an Expression<Func<TEntity, bool>> for multi-level expression


Apologies if this is covered in other answers. I searched all night and went through a hundred other solutions but couldn't quite put the puzzle pieces together. I've experimented with LinqPad, PredicateBuilder, ExpressionVisitors, etc. but I'm still scratching my head.

I am trying to implement something slightly more complicated than this but this code shows the issue:

public Expression<Func<TEntity, bool>> GeneratorEqualityTest<TProperty>(Expression<Func<TEntity, TProperty>> accessor, int expectedValue) {
    // Help
    var argument = Expression.Parameter(typeof(TEntity));
    var accessorArgument = Expression.Property(argument, accessor.ToPropertyName());
    // Help

    Predicate = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(accessorArgument, Expression.Constant(expectedValue)), argument);
}

This works fine when accessor is something like this: x => x.Value

But not with x => x.Complex.Value or x => x.Complex.MoreComplex.Value

I'm trying to parse expressions from strings such as >=5 or (5...10] and generate Expressions that I can plug into the Where clause of a LINQ-to-EF query (and get translated to T-SQL). This is working fine for the single-level case but I can't figure out how to walk the expression.

In EF, x.Complex.MoreComplex.Value corresponds to SQL Join's. Bonus points if it's possible to convert the accessor into something I can pass into an Include() statement

I put a sample project up on Github: https://github.com/scottt732/ExpressionHelp


Solution

  • Since you are receiving the final value accessor, all you need it to apply the comparison function (i.e. build the body of the predicate):

    public Expression<Func<TEntity, bool>> GeneratorEqualityTest<TProperty>(Expression<Func<TEntity, TProperty>> accessor, TProperty expectedValue)
    {
        var body = Expression.Equal(accessor.Body, Expression.Constant(expectedValue));
        var predicate = Expression.Lambda<Func<TEntity, bool>>(body, accessor.Parameters);
        return predicate; 
    }
    

    Bonus method:

    static class ExpressionUtils
    {
        public static IEnumerable<Expression<Func<TEntity, object>>> Includes<TEntity, TProperty>(this Expression<Func<TEntity, TProperty>> accessor)
        {
            var root = accessor.Parameters[0];
            for (var node = accessor.Body as MemberExpression; node != null && node.Expression != root; node = node.Expression as MemberExpression)
                yield return Expression.Lambda<Func<TEntity, object>>(node.Expression, root);
        }
    }