Reputation: 1566
I have two linq expressions I want to combine but my code gives me an error The binary operator And is not defined for the types 'System.Func`2[Web.Entities.Customer,System.Boolean]'
and 'System.Func`2[Web.Entities.Customer,System.Boolean]'
.
I have 2 expressions for example...
Expression<Func<Customer, bool>> filter = c => c.Active;
Expression<Func<Customer, bool>> filterz = c => c.Visible;
i then combine them
filter = Expression.Lambda<Func<Customer, bool>>(Expression.And(filter, filterz));
Any help regarding this problem?
thanks....
here is the updateed code given on the answer below.
public class SwapVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
filter = Expression.Lambda<Func<Customer, bool>>(Expression.AndAlso(
new SwapVisitor(filter.Parameters[0], filterz.Parameters[0]).Visit(filter.Body), filterz.Body), filterz.Parameters)
Upvotes: 2
Views: 374
Reputation: 7445
You already can use your expressions in repository without outer combining them:
var yourContext = getContext();
var filtered = yourContext.Where(filter).Where(filter2);
Combining is unnecessary in this case and this approach will work either without affecting to efficiency.
If you need combining:
Try use the following visitor helper:
public class ReplacementVisitor : System.Linq.Expressions.ExpressionVisitor
{
private readonly Expression _oldExpr;
private readonly Expression _newExpr;
public ReplacementVisitor(Expression oldExpr, Expression newExpr)
{
_oldExpr = oldExpr;
_newExpr = newExpr;
}
public override Expression Visit(Expression node)
{
if (node == _oldExpr)
return _newExpr;
return base.Visit(node);
}
}
We need it because, your filtering expressions use different parameters instances. For combining we need them to use the same parameter instance. This class helps us to do the following:
Expression<Func<Customer, bool>> filter = c => c.Active;
Expression<Func<Customer, bool>> filterz = c => c.Visible;
var newParameter = Expression.Parameter(typeof(Customer), "x");
var visitor1 = new ReplacementVisitor(filter.Parameters[0], newParameter);
var visitor2 = new ReplacementVisitor(filterz.Parameters[0], newParameter);
var newLambda = Expression.Lambda(
Expression.AndAlso(
visitor1.Visit(filter.Body),
visitor2.Visit(filterz.Body)
),
newParameter
);
Upvotes: 1
Reputation: 116108
Expression<Func<Customer, bool>> filter1 = c => c.Active;
Expression<Func<Customer, bool>> filter2 = c => c.Visible;
var parameter = Expression.Parameter(typeof(Customer), "x");
var filter = Expression.Lambda<Func<Customer, bool>>(
Expression.AndAlso(
Expression.Invoke(filter1,parameter),
Expression.Invoke(filter2,parameter)
),parameter
);
Upvotes: 3
Reputation: 2760
if you are working with the same Customer
in each filter you have, than you can try:
Expression<Func<Customer, bool>> filter = c => c.Active;
Expression<Func<Customer, bool>> filter2 = c => c.Visible;
var body = Expression.AndAlso(filter.Body, Expression.Invoke(filter2, filter.Parameters[0]));
filter = Expression.Lambda<Func<Customer, bool>>(body, filter.Parameters);
var applyFilter = filter.Compile();
var customer = new Customer() { Visible = true, Active = true};
Console.WriteLine(applyFilter(customer));
customer.Active = false;
Console.WriteLine(applyFilter(customer));
customer.Visible = false;
Console.WriteLine(applyFilter(customer));
Upvotes: 2