合并两个LINQ表达式

5
我有两个表达式,分别在不同的时间建立,但需要合并才能正确地“分组”where子句。我尝试过这个选项,但我正在使用Entity Framework,它不理解Invoke函数。我看到了一些ExpressionVisitor的替代方案,但我认为我对需要做什么没有足够的理解。
如果有人能指引我正确的方向,我会非常感激,感觉就快成功了。
第一个Where子句 (对象类型为Expression<Func<T, bool>>)
{parm => parm.Name.Contains("hat")}

Where Clause 1B (对象类型为LambdaExpression,但可以使用(Expression<Func<T, bool>>)LambdaExpression) )

{parm => parm.Attributes.Any(parm => ((parm.Name == "test") AndAlso (parm.Value == "21")))}

需要的Where子句

{parm =>
 parm.Name.Contains("hat") (&&/||)
 parm.Attributes.Any(parm => ((parm.Name == "test") AndAlso (parm.Value == "21")))
}

如果有人能帮我合并Where Clause 1AWhere Clause 1B,我将非常感谢。
只是提醒一下,Where()和Any()子句是从IQueryable获得的通用方法,不确定是否重要。
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);

使用Invoke时出现的错误是:

NotSupportedException

在实体框架中,不支持LINQ表达式节点类型“Invoke”。


你能具体展示一下关于 Invoke 的异常是什么吗? - MarcinJuraszek
@MarcinJuraszek 查询提供程序将仅抛出一个异常,指出不支持“Invoke”。为什么特定类型的异常会相关呢? - Servy
@Servy 因为我真的很惊讶LINQ to SQL不支持Expression.Invoke,我在想OP是否正在使用Func.Invoke,我相当确定这会失败。 - MarcinJuraszek
1个回答

15
这是一个不使用InvokePredicateBuilder实现示例:
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);
    }
}

相反,它使用一个Replace方法(以下是实现),用另一个表达式替换所有实例。

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);
    }
}

Great, I am ready to assist you. What do you need help with?

{parm => (parm.Name.Contains("hat") AndAlso parm.Attributes.Any(parm => (parm.Value == "21")))} ... 你,我的朋友,太棒了,还需要4分钟就可以晋升啦 :D #兴奋 - afreeland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接