Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linq谓词生成器、分组和运算符优先级_Linq_Grouping_Operator Precedence_Predicatebuilder - Fatal编程技术网

Linq谓词生成器、分组和运算符优先级

Linq谓词生成器、分组和运算符优先级,linq,grouping,operator-precedence,predicatebuilder,Linq,Grouping,Operator Precedence,Predicatebuilder,以下是问题的一个示例: var source = new LambdasTestEntity[] { new LambdasTestEntity {Id = 1}, new LambdasTestEntity {Id = 2}, new LambdasTestEntity {Id = 3}, new LambdasTestEntity {Id = 4}, }; Exp

以下是问题的一个示例:

   var source = new LambdasTestEntity[] { 
        new LambdasTestEntity {Id = 1},
        new LambdasTestEntity {Id = 2},          
        new LambdasTestEntity {Id = 3},
        new LambdasTestEntity {Id = 4},          
    };

    Expression<Func<LambdasTestEntity, bool>> expression1 = x => x.Id == 1;
    Expression<Func<LambdasTestEntity, bool>> expression2 = x => x.Id == 3;
    Expression<Func<LambdasTestEntity, bool>> expression3 = x => x.Id > 2;

    // try to chain them together in a following rule
    // Id == 1 || Id == 3 && Id > 2
    // as && has higher precedence, we expect getting two entities
    // with Id=1 and Id=3 

    // see how default LINQ works first
    Expression<Func<LambdasTestEntity, bool>> expressionFull = x => x.Id == 1 || x.Id == 3 && x.Id > 2;

    var filteredDefault = source.AsQueryable<LambdasTestEntity>()
              .Where(expressionFull).ToList();

    Assert.AreEqual(2, filteredDefault.Count); // <-this passes

    // now create a chain with predicate builder
    var totalLambda = expression1.Or(expression2).And(expression3);

    var filteredChained = source.AsQueryable<LambdasTestEntity>()
              .Where(totalLambda).ToList();


    Assert.AreEqual(2, filteredChained.Count);
    // <- this fails, because PredicateBuilder has regrouped the first expression,
    // so it now looks like this: (Id == 1 || Id == 3) && Id > 2
如果PredicateBuilder的行为与默认的Linq表达式生成器不同,我发现使用它有点不安全

现在有一些问题:

1) 为什么Linq要创建这些组?即使我创建了一个或表达式

x => x.Id == 1 || x.Id == 3 || x.Id > 2
我仍然将前两个标准分组如下:

((x.Id == 1) OrElse (x.Id == 3)) OrElse (x.Id > 2)
ex = x => x.Id == 1;
ex = ex || x.Id == 3;
ex = ex && x.Id > 2;
为什么不只是

(x.Id == 1) OrElse (x.Id == 3) OrElse (x.Id > 2)
?

2) 为什么PredicateBuilder要添加这些调用?我在默认的Linq表达式结果中没有看到调用,所以它们看起来没用

3) 是否有其他方法来构造表达式“脱机”,然后传递给默认的Linq表达式生成器?大概是这样的:

((x.Id == 1) OrElse (x.Id == 3)) OrElse (x.Id > 2)
ex = x => x.Id == 1;
ex = ex || x.Id == 3;
ex = ex && x.Id > 2;
然后Linq表达式生成器对其进行解析,并创建与它对x=>x.Id==1 | | x.Id==3&&x.Id>2所做的相同的表达式(提供更高的优先级(&p)?
或者我也可以调整PredicateBuilder来做同样的事情?

扩展我上面的评论:


因为这里没有运算符优先级的概念。你是 从字面上说,自己构建表达式树,并“管道化”表达式 从一个方法到下一个方法的结果决定了顺序。因此,这个顺序 结果表达式将与您指定的完全相同

PredicateBuilder
的完整源代码已经发布,显示了它是多么简单。但它也向您显示了上述问题的根源。如果您不想访问Albahari的网站,以下是完整的来源:

public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                 Expression<Func<T, bool>> expr2)
{
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
         (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}

public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                  Expression<Func<T, bool>> expr2)
{
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
         (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
公共静态表达式或(此表达式expr1,
表达式expr2)
{
var invokedExpr=Expression.Invoke(expr2,expr1.Parameters.Cast());
返回表达式.Lambda
(Expression.OrElse(expr1.Body,invokedExpr),expr1.Parameters);
}
公共静态表达式和(此表达式expr1,
表达式expr2)
{
var invokedExpr=Expression.Invoke(expr2,expr1.Parameters.Cast());
返回表达式.Lambda
(Expression.AndAlso(expr1.Body,invokedExpr),expr1.Parameters);
}
这里需要注意的主要问题是,它一次构建一个节点的表达式,然后将此节点作为后续节点的左表达式(叶)进行管道传输。
Expression.Invoke
调用只是将现有节点的参数通过管道传输到右叶(下一个表达式),其余的都是不言自明的


编辑:我必须做类似的事情(但没有使用PredicateBuilder,而是自己使用
Expression
调用构建树)。需要记住的主要事情是,您只需首先处理
和/和also
节点,然后处理
或/或lse
节点,这样您就可以以适当的优先级构建树。不幸的是,手工构建
ExpressionTrees
是一个循序渐进的过程,因此,您必须确保将每个步骤分解为正确的顺序,以获得您想要/需要的结果。

这并不是因为
&
在c中的
|
优先于扩展方法
或PredicateBuilder中的
。您可以只执行
expression1.或(expression2.和(expression3))但我不知道这是否是您想要的,或者它是否有效)而这正是问题所在-C#和默认Linq表达式生成器都提供了更高的优先级,但PredicateBuilder。有时它可能会令人困惑。嗯,对于(我认为至少是这样)大多数人来说,运算符优先级的使用不像好的旧括号那样可读(或“第一眼”就可以理解)。。。所以,带括号的解决方案不适合你?我可以接受。我想,这只是我的好奇心——我想知道这种分组在哪一点发生,以及为什么它会发生,即使根本不需要它(比如链接多个Orelse)。因为这里没有运算符优先级的概念。您实际上是自己构建表达式树,并将一个方法的结果“管道化”到下一个方法,从而确定顺序。因此,结果表达式的顺序将与您指定的顺序完全相同。谢谢,现在我更好地理解了为什么会发生这种分组——因为逻辑操作被处理为二叉树,其中每个操作总是有左/右两侧。我还发现了PredicateBuilder的另一种选择:它看起来有点复杂,但它不使用调用,所以我想它可能更通用。这不仅仅限于二进制操作,而是所有表达式的表示方式。查看如何将表达式分解为树的每个节点。