Grouping lambda expressions with statements and using them with PredicateGroups DapperExtensions

According to my previous question: Pulling Apart Expression<Func<T, object>> - I'm trying to make it a little more advanced. Currently I can do this:

 var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId); 

which will be converted to 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); 

Now I want to be able to do this:

 var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"); 

and generate SQL that looks something like this:

 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) 

using the 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} }; 

My problem seems to be related to how expression trees are expressed. Suppose we have a lambda expression:

 p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack" 

I can apply this to BinaryExpression . If I use BinaryExpression.Left , I get

 p.MarketId == marketId && p.FirstName == "John" 

and BinaryExpression.Right gives:

 p.FirstName == "Jack" 

Furthermore, the NodeType for the generic BinaryExpression seems to be set by the last conditional lambda operator, i.e. ExpressionType.OrElse

It seems to me that I need to use recursion and traverse the lambda expression from right to left, but I was not able to create complex group predicates that I want. In particular, how do I group AND lambdas together, and OR lambdas together? Thanks!

+4
source share
1 answer

Your lambda example,

 p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack" 

is equivalent

 p => (p.MarketId == marketId && p.FirstName == "John") || p.FirstName == "Jack" 

since && has higher precision than || .

Because of this, you get a tree with && at the bottom (as you need to calculate it first), and then || on top:

  || / \ && Firstname == "Jack" / \ p.MarketId == marketId p.FirstName == "John" 

Once you understand the priority of the operator, this makes sense. If you want to use an alternative, you can simply use parentheses to first evaluate || (so that it hits the bottom of the expression tree).

 p => p.MarketId == marketId && (p.FirstName == "John" || p.FirstName == "Jack") 

Your common problem is that you are approaching this a little wrong. You are currently trying to create 2 groups, one for OR and one for AND. This may work in this case, but not in general, for example, what would you do for this: (a && b) || (c && d) (a && b) || (c && d) ?

I think that something should happen that everyone, and everyone, or should translate into their own group of predicates. See the β€œMultiple Compound Predicates (Predicate Group)” section of your related article. You simply replace BinaryExpression with a predicate group.

In your example, you have (a && b) || c with || on the top. What you want in the end is every time you have an operator that you want to create a predicate group with a left and right list of expressions. In your logic, to convert a binary expression to a predicate group, you must first use the same function to convert the left and right to the predicate group

i.e. your code sees ||.
he creates a group of predicates ready to add expressions to his list
he first selects the left one (it doesn't matter).
ok, left is another binary expression, so it calls itself to get a predicate group that your target library understands so that recursion only works with && b. First, he selects the left one, sees a simple predicate, which means that he can just add it to the predicate group, he does the same for the correct one, then we return to the original function call, which now has the predicate group, the lower expression on the left is converted to another predicate group and is added. he goes right now, which is a simple individual predicate, and therefore he can add it to his list.

Okay, so if I had something crazy, for example: a && b || c || d && e a && b || c || d && e

Well, given the higher priority, we get the following: ((a && b) || c) || (d && e) ((a && b) || c) || (d && e) note that I am not 100% sure that either c with the first && or the last will be added in the bracket, but it does not matter, the logic is the same. When rendering the tree, start with the innermost brackets. These are the β€œleaves”, then work outward using the brackets to process the tree, and finally, reaching the root of the node, which in our case is equal to || to the right of c:

  || / \ || && / \ / \ && cde / \ ab 
+2
source

All Articles