You need to unwrap the bodies of your lambdas, rather than using Expression.Invoke
. You'll also have to rewrite at least one of the lambdas, so they both use the same parameters.
We'll use an ExpressionVisitor
to replace right's parameter with the corresponding parameter from left. Then we'll construct the AndExpression
using left's body and the rewritten body from right, and finally create a new lambda.
public class ParameterReplaceVisitor : ExpressionVisitor
{
public ParameterExpression Target { get; set; }
public ParameterExpression Replacement { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Target ? Replacement : base.VisitParameter(node);
}
}
public static Expression<Func<T, bool>> AndExpression<T>(
Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
var visitor = new ParameterReplaceVisitor()
{
Target = right.Parameters[0],
Replacement = left.Parameters[0],
};
var rewrittenRight = visitor.Visit(right.Body);
var andExpression = Expression.AndAlso(left.Body, rewrittenRight);
return Expression.Lambda<Func<T, bool>>(andExpression, left.Parameters);
}
This results in a lambda with the following DebugView:
.Lambda #Lambda1<System.Func`2[System.String,System.Boolean]>(Entity $t) {
$t.Id == "id1" && $t.Id == "id2"
}
Whereas your code results in a lambda with the following DebugView:
.Lambda #Lambda1<System.Func`2[System.String,System.Boolean]>(System.String $t) {
$t == "id1" && .Invoke (.Lambda #Lambda2<System.Func`2[System.String,System.Boolean]>)($t)
}
.Lambda #Lambda2<System.Func`2[System.String,System.Boolean]>(System.String $t) {
$t == "id2"
}
See how yours is calling a lambda from within a lambda (something that EF can't handle), whereas mine just has a single lambda.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…