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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With