根据我之前的问题:
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