Вам нужно развернуть тела своих лямбд, а не использовать Expression.Invoke
. Вам также придется переписать хотя бы одну из лямбд, чтобы они оба использовали одинаковые параметры.
Мы будем использовать ExpressionVisitor
, чтобы заменить параметр right соответствующим параметром left. Затем мы сконструируем AndExpression
, используя тело слева и переписанное тело справа, и, наконец, создадим новую лямбду.
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);
}
Это приведет к лямбде со следующим DebugView:
.Lambda #Lambda1<System.Func`2[System.String,System.Boolean]>(Entity $t) {
$t.Id == "id1" && $t.Id == "id2"
}
В то время как ваш код приводит к лямбде со следующим 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"
}
Посмотрите, как ваш вызывает лямбда изнутри лямбда (то, что EF не может обработать), тогда как мой просто имеетодна лямбда.