Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
308 views
in Technique[技术] by (71.8m points)

c# - How does PredicateBuilder work

C# in a Nutshell has a free class called PredicateBuilder which constructs LINQ predicates piece by piece available here. Here's an extract of the method which adds a new expression to the predicate. Could someone explain it? (I have seen this question, I don't want a general answer like there. I am looking for a specific explanation of how Expression.Invoke and Expression.Lambda build the new expression).

public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                     Expression<Func<T, bool>> expr2)
{
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
        (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Let's say you have:

Expression<Func<Person, bool>> isAdult = p1 => p1.Age >= 18;

// I've given the parameter a different name to allow you to differentiate.
Expression<Func<Person, bool>> isMale = p2 => p2.Gender == "Male";

And then combine them with PredicateBuilder

var isAdultMale = isAdult.And(isMale);

What PredicateBuilder produces is an expression that looks like this:

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

As you can see:

  1. The resulting lambda reuses the parameters of the first expression.
  2. Has a body that invokes the second expression by passing the parameters of the first expression as a replacement for the second expression's parameters. The resulting InvocationExpression is sort of like the expression-equivalent of a method-call (calling a routine by passing in arguments for parameters).
  3. Ands the first expression's body and this InvocationExpression together to produce the body of the resulting lambda.

The idea is that the LINQ provider should be able to understand the semantics of this operation and take a sensible course of action (e.g. generate SQL like WHERE age >= 18 AND gender = 'Male').

Often though, providers have problems with InvocationExpressions, because of the obvious complications of processing a 'nested expression-call inside an expression.'

To get around this, LINQKit also provides the Expand helper. This essentially 'inlines' the invocation call smartly by replacing the call with the body of the nested expression, substituting uses of the nested expression's parameters appropriately (in this case, replacing p2 with p1). This should produce something like:

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

Note that this how you would have manually combined those predicates if you'd done it yourself in a lambda. But with LINQKit around, you can get these predicates from independent sources and easily combine them:

  1. Without writing "by hand" expression code.
  2. Optionally, in a way that is transparent to consumers of the resulting lambda.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...