c# – 由运算符对Lambda表达式进行分组,并将它们与DapperExtensions的PredicateGroups一起使用

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

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

将转换为DapperExtensions FieldPredicate

// 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)

使用DapperExtensions Compound 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"

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

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

最佳答案 你的例子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)?

我认为应该发生的是每个和每个或应该转换成它自己的谓词组.请参阅链接文章的“多个复合谓词(谓词组)”部分.您只需将BinaryExpression替换为谓词组即可.

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

即你的代码看到了||.
它创建了一个谓词组,准备将表达式添加到其列表中
它先选择左边(无所谓).
好的,左边是另一个二进制表达式,所以它调用自己来获得目标库理解的谓词分组
因此,递归仅适用于&&湾它首先选择左边,看一个简单的谓词,这意味着它可以简单地将它添加到谓词组,它在右边做同样的事情
然后我们回到对函数的原始调用,该函数现在有一个谓词组,左边的较低表达式转换为不同的谓词组并添加.
它现在正在向下,这是一个简单的单个谓词,因此它可以将其添加到其列表中.

Okay, so if I had something crazy like: a && b || c || d && e

好的,考虑到更高的优先级,我们得到以下结果:
((a&& b)|| c)|| (d& e)
请注意,我不是100%肯定支架会将c与第一个&&或者是最后一个,但没关系,逻辑是一样的
可视化树时,从最里面的括号开始.他们是“叶子”
然后向外工作,使用括号来处理树,最后到达根节点,在我们的例子中是||在c的右边:

       ||
     /    \
    ||     &&
   /  \   /  \
  &&   c d   e
 /  \
a    b
点赞