【问题标题】:Merge two linq expressions合并两个 linq 表达式
【发布时间】:2014-03-21 20:36:07
【问题描述】:

我有两个在不同时间构建的表达式,但需要合并才能对 where 子句进行准确的“分组”。我确实尝试了this 选项,但我使用的是实体框架,它不理解Invoke 函数。我已经看到了一些 ExpressionVisitor 替代方案,但我认为我对自己需要做的事情没有足够的了解。

如果有人能指出我正确的方向,我将不胜感激,感觉它就在附近。

Where Clause 1A (对象类型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 to Entities 不支持 LINQ 表达式节点类型“Invoke”。

【问题讨论】:

  • 您能否准确说明您遇到了关于Invoke 的哪种异常?
  • @MarcinJuraszek 查询提供程序只会抛出一个异常,指出不支持Invoke。为什么特定类型的异常是相关的?
  • @Servy 因为我真的很惊讶 LINQ to SQL 不支持 Expression.Invoke 并且我想知道 OP 是否没有使用 Func.Invoke 代替,我很确定这会失败.

标签: c# linq entity-framework expression linq-expressions


【解决方案1】:

这是一个不使用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 将两个采用相同输入的谓词表达式“与”。

【讨论】:

  • {parm => (parm.Name.Contains("hat") AndAlso parm.Attributes.Any(parm => (parm.Value == "21")))} ... 你我的朋友,太棒了,宣传还需要 4 分钟 :D #PUMPED
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
  • 2021-12-30
相关资源
最近更新 更多