There are two problems here. The first is that if you use Expression.And
, this is equivalent to the binary operator &
, which is probably not what you want. You have to write
Expression.AndAlso(e1, e2)
to get the equivalent of e1 && e2
.
The bigger problem is that you are trying to combine two lambda expressions with &&
, which is not possible because &&
works on boolean expressions. What you want is, given two lambda expressions lambda1 = x => e1
and lambda2 = x => e2
, return x => e1 && e2
.
In order to achieve this, follow these steps:
- Create a new
ParameterExpression
p
.
- Replace the parameter in
lambda1.Body
by p
(you can do this with an ExpressionVisitor
) to get renamedBody1
.
- Replace the parameter in
lambda2.Body
by p
to get renamedBody2
.
- Now you can construct
Expression.Lambda<Func<Item, bool>>(
Expression.AndAlso(renamedBody1, renamedBody2), p);
This is the expression you can use as argument of the Where
method.
Note: you can avoid the parameter renaming if you make sure that you use the same parameter expression while constructing the two lambda expressions, using Expression.Lambda()
. But then you can't construct them as elegantly using the =>
syntax as you did in your code, which produces different parameters even though they are named item
in both lambda expressions.
The full code
Alternatively, just use the And
extension method in the following code (lambda1.And(lambda2)
):
/// <summary>
/// Extension methods related to <see cref="Expression" />.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Renames all parameter names equal to <paramref name="oldParameterName" /> in
/// <paramref name="expression" /> to <paramref name="newParameter" />.
/// </summary>
[NotNull]
public static Expression ReplaceParameter([NotNull] this Expression expression,
[NotNull] string oldParameterName,
[NotNull] ParameterExpression newParameter)
{
if (expression == null) throw new ArgumentNullException(nameof(expression));
if (oldParameterName == null) throw new ArgumentNullException(nameof(oldParameterName));
if (newParameter == null) throw new ArgumentNullException(nameof(newParameter));
AlphaRenamingExpressionVisitor visitor =
new AlphaRenamingExpressionVisitor(oldParameterName, newParameter);
return visitor.Visit(expression).AsNotNull();
}
/// <summary>
/// Returns the conjunction of the two given predicate expressions, performing alpha renamings if necessary.
/// </summary>
[NotNull]
public static Expression<Func<T, bool>> And<T>([NotNull] this Expression<Func<T, bool>> e1,
[NotNull] Expression<Func<T, bool>> e2)
{
if (e1 == null) throw new ArgumentNullException(nameof(e1));
if (e2 == null) throw new ArgumentNullException(nameof(e2));
return binaryOperation(e1, e2, Expression.AndAlso);
}
/// <summary>
/// Returns the negation of the given predicate expression.
/// </summary>
[NotNull]
public static Expression<Func<T, bool>> Not<T>([NotNull] this Expression<Func<T, bool>> e1)
{
if (e1 == null) throw new ArgumentNullException(nameof(e1));
return Expression.Lambda<Func<T, bool>>(Expression.Not(e1.Body),
e1.Parameters);
}
/// <summary>
/// Returns the disjunction of the two given predicate expressions, performing alpha renamings if necessary.
/// </summary>
[NotNull]
public static Expression<Func<T, bool>> Or<T>([NotNull] this Expression<Func<T, bool>> e1,
[NotNull] Expression<Func<T, bool>> e2)
{
if (e1 == null) throw new ArgumentNullException(nameof(e1));
if (e2 == null) throw new ArgumentNullException(nameof(e2));
return binaryOperation(e1, e2, Expression.OrElse);
}
[NotNull]
private static Expression<Func<T, bool>> binaryOperation<T>([NotNull] Expression<Func<T, bool>> e1,
[NotNull] Expression<Func<T, bool>> e2, [NotNull] Func<Expression, Expression, Expression> binaryOp)
{
if (binaryOp == null) throw new ArgumentNullException(nameof(binaryOp));
if (e1.Parameters[0].Equals(e2.Parameters[0]))
{
return Expression.Lambda<Func<T, bool>>(binaryOp(e1.Body, e2.Body), e1.Parameters[0]);
}
ParameterExpression newParam = Expression.Parameter(typeof(T), "x" + Guid.NewGuid().ToString("N"));
Expression renamedBody1 = e1.Body.ReplaceParameter(e1.Parameters[0].Name, newParam);
Expression renamedBody2 = e2.Body.ReplaceParameter(e2.Parameters[0].Name, newParam);
return Expression.Lambda<Func<T, bool>>(binaryOp(renamedBody1, renamedBody2),
newParam);
}
private class AlphaRenamingExpressionVisitor : ExpressionVisitor
{
[NotNull] private readonly string oldParameterName;
[NotNull] private readonly ParameterExpression newParameter;
/// <summary>
/// Initializes a new instance of <see cref="AlphaRenamingExpressionVisitor" /> with the given parameters.
/// </summary>
public AlphaRenamingExpressionVisitor([NotNull] string oldParameterName,
[NotNull] ParameterExpression newParameter)
{
this.oldParameterName = oldParameterName ?? throw new ArgumentNullException(nameof(oldParameterName));
this.newParameter = newParameter ?? throw new ArgumentNullException(nameof(newParameter));
}
/// <inheritdoc />
[NotNull]
protected override Expression VisitParameter([NotNull] ParameterExpression node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
return node.Name == oldParameterName ? newParameter : node;
}
}
}