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

标签 c# extension-methods dapper

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

相关文章:

c# - 为什么 IEnumerable 上没有 ForEach 扩展方法?

javascript - 使用 (jQuery) 执行函数

asp.net - 关于ADO.NET、Dapper QueryAsync和Glimpse.ADO组合的一些问题

c# - 无法在异步方法中使用 toList() 进行转换

c# - 如何格式化 ListView 列以显示货币符号

C#线程同步

c# - 创建通用扩展方法来获取值的显示名称属性字符串列表

c# - Dapper 建筑参数表

c# - 如何在 Windows Phone 7 中将 Image 和 videoBrush 合并到一个图像中?

c# - 在 C# 中合并 2 个文本文件