合并两个linq表达式

本文关键字:两个 linq 表达式 合并 | 更新日期: 2023-09-27 17:51:19

我有两个在不同时间构建的表达式,但需要合并以获得where子句的准确"分组"。我确实尝试了这个选项,但我正在使用实体框架,它不理解Invoke函数。我已经看到了一些ExpressionVisitor替代方案,但我不认为我对我需要做的事情有足够的了解。

如果有人能告诉我正确的方向,我将不胜感激,感觉就在那里。

Where子句1 (对象类型Expression<Func<T, bool>>)

{parm => parm.Name.Contains("hat")}

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

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

Needed 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 to Entities中不支持LINQ表达式节点类型'Invoke'。

合并两个linq表达式

下面是不使用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);
    }
}

使用这个,您现在可以使用And将两个接受相同输入的谓词表达式和在一起。