根据我之前的问题: 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"
另外,NodeType
整体BinaryExpression
似乎设置为 lambda 的最后一个条件运算符,即 ExpressionType.OrElse
我觉得我需要使用递归并从右到左遍历 lambda 表达式,但我一直无法创建我想要的复合组谓词。具体来说,我如何对 AND
进行分组lambda 一起,和 OR
lambda 一起?谢谢!
最佳答案
您的示例 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)
?
我认为应该发生的是 each and each or 应该翻译成它自己的谓词组。请参阅链接文章的“多个复合谓词(谓词组)”部分。您只需用谓词组替换 BinaryExpression。
在你的例子中你有 (a && b) || c 与 ||在顶部。你想要结束的是每次你有一个运算符时你想要创建一个谓词组,用 left 和 right 作为表达式列表。 在将二进制表达式转换为谓词组的逻辑中,您将首先使用相同的函数将左侧和右侧转换为谓词组
即你的代码看到了 ||。
它创建一个谓词组准备将表达式添加到它的列表
它先选择左(无关紧要)。
好的,left 是另一个二进制表达式,所以它调用自身来获得目标库理解的谓词组
所以递归只对 a && b 起作用。它首先选择左边,看到一个简单的谓词,a 这意味着它可以简单地将它添加到谓词组,它对右边做同样的事情
然后我们回到对函数的原始调用,该函数现在有一个谓词组,左侧的较低表达式转换为不同的谓词组并添加。
它现在向下移动,这是一个简单的单独谓词,因此它可以将它添加到它的列表中。
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
关于c# - 通过运算符对 Lambda 表达式进行分组并将它们与 DapperExtensions 的 PredicateGroups 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16083895/