4

根据我之前的问题:Pulling Apart Expression<Func<T, object>>- 我正试图让它更先进一点。目前,我可以这样做:

var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId);

它将被转换为DapperExtensionsFieldPredicate

// Assume I've successfully parsed p => p.MarketId == marketId into its constituent parts:
// left = p => p.MarketId, theOperator = Operator.Eq, right = marketId
Predicates.Field(left, theOperator, right);

我现在希望能够做到这一点:

var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack");

并生成如下所示的 SQL:

DECLARE @MarketId INT = 3
DECLARE @FirstName01 VARCHAR(MAX) = 'John'
DECLARE @FirstName02 VARCHAR(MAX) = 'Jack'

SELECT *
FROM Person
WHERE MarketId = @MarketId AND (FirstName = @FirstName01 OR FirstName = @FirstName02)

通过使用DapperExtensionsCompound Predicate Groups

// ** This is the code I am trying to dynamically create based on the lambda that is passed in **

var predicateGroupAnd = new PredicateGroup {Operator = GroupOperator.And, Predicates = new List<IPredicate>()};
// I already have the code to determine: left = p => p.MarketId, theOperator = Operator.Eq, right = marketId
predicateGroupAnd.Predicates.Add(Predicates.Field(left, Operator.Eq, right));

var predicateGroupOr = new PredicateGroup {Operator = GroupOperator.Or, Predicates = new List<IPredicate>()};
// I already have the code to determine: left = p => p.FirstName, theOperator = Operator.Eq, right = "John"
predicateGroupAnd.Predicates.Add(Predicates.Field(left, Operator.Eq, right));
// I already have the code to determine: left = p => p.FirstName, theOperator = Operator.Eq, right = "Jack"
predicateGroupOr.Predicates.Add(Predicates.Field(left, Operator.Eq, right));

var predicateGroupAll = new PredicateGroup // This is what will be passed to DapperExtensions' GetList<T> method
    {
        Operator = GroupOperator.And, // How do I set this correctly?
        Predicates = new List<IPredicate> {predicateGroupAnd, predicateGroupOr}
    };

我的问题似乎与解析表达式树的方式有关。假设我们有 lambda 表达式:

p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"

我可以将其转换为BinaryExpression. 如果我使用BinaryExpression.Left,我会得到

p.MarketId == marketId && p.FirstName == "John"

BinaryExpression.Right产量:

p.FirstName == "Jack"

此外,NodeType整体的BinaryExpression似乎设置为 lambda 的最后一个条件运算符,即ExpressionType.OrElse

我觉得我需要使用递归并从右到左遍历 lambda 表达式,但我无法创建我想要的复合组谓词。具体来说,我如何将ANDlambdas 组合在一起,以及ORlambdas 组合在一起?谢谢!

4

1 回答 1

2

您的示例 lambda,

p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"

是等价的

p => (p.MarketId == marketId && p.FirstName == "John") || p.FirstName == "Jack"

因为&&比 具有更高的优先级||

因此,您将&&在“底部”得到一棵树,(因为它需要先计算)然后||在顶部:

                         ||
                        /  \
                       &&  Firstname == "Jack"
                      /  \
p.MarketId == marketId    p.FirstName == "John"

一旦您了解了运算符优先级,上述内容就有意义了。如果你想要一个替代方案,你可以使用括号来强制||首先评估(使其最终位于表达式树的底部)。

p => p.MarketId == marketId && (p.FirstName == "John" || p.FirstName == "Jack")

你的一般问题是你正在接近这个有点错误。目前您正在尝试创建 2 个组,一个用于 OR,一个用于 AND。这可能适用于这种情况,但不适用于一般情况,例如,你会为此做什么:(a && b) || (c && d)

我认为应该发生的是,每个 and 和每个 or 都应该转化为自己的谓词组。请参阅链接文章的“多个复合谓词(谓词组)”部分。您只需将 BinaryExpression 替换为谓词组。

在您的示例中,您有 (a && b) || c 与 || 在顶部。你想要结束的是每次你有一个运算符你想创建一个谓词组,左和右作为表达式列表。在将二进制表达式转换为谓词组的逻辑中,您将首先使用相同的函数将左侧和右侧转换为谓词组

即你的代码看到||。
它创建一个谓词组,准备将表达式添加到它
首先选择的列表中(没关系)。
好的,left 是另一个二进制表达式,因此它调用自身来获取目标库可以理解的谓词组,
以便递归仅对 && b 起作用。它首先选择左边,看到一个简单的谓词,a 这意味着它可以简单地将它添加到谓词组,它对右边做同样的事情,
然后我们回到对现在有一个谓词组的函数的原始调用左侧较低的表达式转换为不同的谓词组并添加。它现在下降,这是一个简单的单个谓词,因此它可以将它添加到它的列表中。

好的,所以如果我有一些疯狂的事情,比如:a && b || c || d && e

好的,考虑到更高的优先级,我们得到以下结果: ((a && b) || c) || (d && e) 请注意,我不是 100% 确定括号会将 c 与第一个 && 或最后一个放在一起,但没关系,逻辑是相同的树,从最里面的括号开始。它们是“叶子”,然后向外工作,使用括号向上工作,最后到达根节点,在我们的例子中是 || 在 c 的右侧:

       ||
     /    \
    ||     &&
   /  \   /  \
  &&   c d   e
 /  \
a    b
于 2013-04-22T08:11:25.283 回答