6

I have two expressions that are built out at separate times, but need to be merged in order to get an accurate 'grouping' of a where clause. I did try this option, but I am using Entity Framework and it doesn't understand the Invoke function. I have seen some of the ExpressionVisitor alternative but I do not think I have a good enough understanding of what I need to do.

If anyone could please point me in the right direction I would much appreciate it, it feels like it is close to there.

Where Clause 1A (object type Expression<Func<T, bool>>)

{parm => parm.Name.Contains("hat")}

Where Clause 1B (object type LambdaExpression, but can use (Expression<Func<T, bool>>)LambdaExpression )

{parm => parm.Attributes.Any(parm => ((parm.Name == "test") AndAlso (parm.Value == "21")))}

Needed Where Clause

{parm =>
 parm.Name.Contains("hat") (&&/||)
 parm.Attributes.Any(parm => ((parm.Name == "test") AndAlso (parm.Value == "21")))
}

If someone could please help me merge Where Clause 1A and Where Clause 1B, I would be very thankful..

Just an FYI Where() and Any() clause are generic methods obtained from IQueryable, not sure if that matters.

Func<MethodInfo, bool> methodLambda = m => m.Name == "Any" && m.GetParameters().Length == 2;
MethodInfo method = typeof(Queryable).GetMethods().Where(methodLambda).Single().MakeGenericMethod(ActualQueryableType);
    
ParameterExpression parentMember = Expression.Parameter(typeof(T), "parentParm");

 // Create Any() Expression tree and convert it to lambda
MethodCallExpression callAny = Expression.Call(method, member, exp);
LambdaExpression lambdaAny = Expression.Lambda(callAny, param);


var combExp = parentExp.And((Expression<Func<T, bool>>)lambdaAny);
                

MethodCallExpression whereCall = Expression.Call(typeof(Queryable), "Where", new Type[] { query.ElementType }, new Expression[] {
query.Expression,
Expression.Quote(combExp)
});

query = (IQueryable<T>)query.Provider.CreateQuery(whereCall);

The error I get when using Invoke is:

NotSupportedException

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

3
  • Could you show exactly what kind of exception about Invoke you get? Commented Mar 21, 2014 at 20:38
  • @MarcinJuraszek The query provider will just throw an exception saying that Invoke isn't supported. Why would the specific type of exception be relevant?
    – Servy
    Commented Mar 21, 2014 at 20:39
  • @Servy Because I'm really surprised that LINQ to SQL does not support Expression.Invoke and I'm wondering if OP isn't using Func.Invoke instead, which I'm pretty sure would fail. Commented Mar 21, 2014 at 20:41

1 Answer 1

17

Here's an implementation of PredicateBuilder that doesn't use Invoke:

public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
    }
}

It instead uses a Replace method (implementation below) that replaces all instances of one expression with another.

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

Using this you can now use And to AND together two predicate expressions that take the same input.

1
  • {parm => (parm.Name.Contains("hat") AndAlso parm.Attributes.Any(parm => (parm.Value == "21")))} ... You my friend, are amazing, it requires another 4 minutes to promote :D #PUMPED
    – afreeland
    Commented Mar 21, 2014 at 20:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.