PredicateBuilder是如何工作的

Nutshell中的C#有一个名为PredicateBuilder的免费类,它可以在这里逐个构造LINQ谓词。 这是该方法的一个摘录,它为谓词添加了一个新表达式。 有人可以解释一下吗? (我已经看到了这个问题 ,我不想在那里得到一般答案。我正在寻找Expression.Invoke和Expression.Lambda如何构建新表达式的具体解释)。

public static Expression<Func> And (this Expression<Func> expr1, Expression<Func> expr2) { var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast ()); return Expression.Lambda<Func> (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters); } 

假设你有:

 Expression> isAdult = p1 => p1.Age >= 18; // I've given the parameter a different name to allow you to differentiate. Expression> isMale = p2 => p2.Gender == "Male"; 

然后将它们与PredicateBuilder结合使用

 var isAdultMale = isAdult.And(isMale); 

PredicateBuilder生成的是一个如下所示的表达式:

 // Invoke has no direct equivalent in C# lambda expressions. p1 => p1.Age >= 18 && Invoke(p2 => p2.Gender == "Male", p1) 

如你看到的:

  1. 生成的lambda 重用第一个表达式的参数。
  2. 具有通过传递第一个表达式的参数作为第二个表达式参数的替换来调用第二个表达式的主体。 生成的InvocationExpression有点像方法调用的表达式等价物(通过传入参数的参数来调用例程)。
  3. And第一个表达式的主体和这个InvocationExpression一起产生了生成的lambda的主体。

这个想法是LINQ提供者应该能够理解这个操作的语义并采取合理的行动方案(例如生成像WHERE age >= 18 AND gender = 'Male' SQL)。

通常情况下,提供程序存在InvocationExpression的问题,因为在表达式中处理“嵌套表达式调用”会有明显的复杂性。

为了解决这个问题,LINQKit还提供了Expand帮助器。 这通过用嵌套表达式的主体替换调用来巧妙地“内联”调用调用,适当地替换嵌套表达式参数的使用(在这种情况下,用p1替换p2 )。 这应该产生类似的东西:

 p1 => p1.Age >= 18 && p1.Gender == "Male" 

请注意,如果您自己在lambda中完成它,那么您将如何手动组合这些谓词。 但是使用LINQKit,您可以从独立源获取这些谓词并轻松组合它们:

  1. 无需编写“手工”表达式代码。
  2. 可选地,以对所得lambda的消费者透明的方式。