Nach meiner vorherigen Frage: Pulling Apart Expression<Func<T, object>>
- Ich versuche, es ein bisschen fortgeschrittener zu machen. Momentan kann ich das machen:
var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId);
welches in ein 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);
Ich möchte das jetzt tun können:
var matchingPeople = Connection.Get<Person>(p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack");
und generieren Sie SQL, das ungefähr so aussieht:
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)
Verwenden von 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}
};
Mein Problem scheint in der Art zu liegen, wie Ausdrucksbäume geparst werden. Angenommen, wir haben den Lambda-Ausdruck:
p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"
Ich kann dies in eine BinaryExpression
. Wenn ich BinaryExpression.Left
, bekomme ich
p.MarketId == marketId && p.FirstName == "John"
und BinaryExpression.Right
liefert:
p.FirstName == "Jack"
Außerdem scheint der NodeType
der gesamten BinaryExpression
auf den letzten Bedingungsoperator des Lambda gesetzt zu sein, dh ExpressionType.OrElse
Ich habe das Gefühl, dass ich die Rekursion benutzen und den Lambda-Ausdruck von rechts nach links durchqueren muss, aber ich war nicht in der Lage, die Verbundgruppenprädikate zu erstellen, die ich möchte. Genauer gesagt, wie gruppiere ich die AND
Lambdas zusammen und die OR
Lambdas zusammen? Vielen Dank!
Ihr Beispiel Lambda,
p => p.MarketId == marketId && p.FirstName == "John" || p.FirstName == "Jack"
ist das Äquivalent von
p => (p.MarketId == marketId && p.FirstName == "John") || p.FirstName == "Jack"
weil &&
einen höheren Vorrang hat als ||
.
Aus diesem Grund erhalten Sie einen Baum mit dem &&
am "Boden", (wie es zuerst berechnet werden muss) und dem ||
oben drauf:
||
/ \
&& Firstname == "Jack"
/ \
p.MarketId == marketId p.FirstName == "John"
Sobald Sie die Vorrangstellung des Operators verstanden haben, macht das obige Sinn. Wenn Sie eine Alternative wünschen, können Sie einfach Klammern verwenden, um das ||
zu erzwingen zuerst ausgewertet werden (so dass es am Ende des Ausdrucksbaums endet).
p => p.MarketId == marketId && (p.FirstName == "John" || p.FirstName == "Jack")
Ihr generelles Problem ist, dass Sie das etwas falsch angehen. Derzeit versuchen Sie, zwei Gruppen zu erstellen, eine für ORs und eine für ANDs. Dies könnte in diesem Fall funktionieren, aber nicht in der allgemeinen, zum Beispiel, was würdest du dafür tun: (a && b) || (c && d)
?
Ich denke, was passieren sollte ist, dass jeder und jeder oder sollte in seine eigene Prädikatsgruppe übersetzen. Siehe den Abschnitt "Mehrere zusammengesetzte Prädikate (Prädikatsgruppe)" in Ihrem verknüpften Artikel. Sie würden einfach BinaryExpression durch eine Prädikatgruppe ersetzen.
In Ihrem Beispiel haben Sie (a && b) || c mit dem || oben. Sie möchten, dass jedes Mal, wenn Sie einen Operator haben, eine Prädikatsgruppe mit der linken und rechten als Liste von Ausdrücken erstellt werden soll. In Ihrer Logik, um einen binären Ausdruck in eine Prädikat-Gruppe zu konvertieren, würden Sie zuerst dieselbe Funktion verwenden, um die linke und die rechte in eine Prädikat-Gruppe umzuwandeln
dh Ihr Code sieht die ||.
Es erstellt eine Prädikatgruppe, die bereit ist, die Ausdrücke zu ihrer Liste hinzuzufügen
es wählt links zuerst (ist nicht wichtig).
ok, links ist ein weiterer binärer Ausdruck, also ruft er sich selbst auf, um eine prädiktive Gruppe zu erhalten, die die Zielbibliothek versteht
damit die Rekursion nur an einem && b arbeitet. Es wählt links zuerst, sieht ein einfaches Prädikat, ein, was bedeutet, dass es einfach zur Prädikatgruppe hinzufügen kann, tut es das gleiche für das Recht
dann sind wir wieder bei dem ursprünglichen Aufruf der Funktion, die jetzt eine Prädikatgruppe mit dem niedrigeren Ausdruck auf der linken Seite hat, der in eine andere Prädikatgruppe umgewandelt und hinzugefügt wurde. es geht jetzt nach rechts, das ist einfach ein individuelles Prädikat und so kann es es zu seiner Liste hinzufügen.
Okay, wenn ich etwas Verrücktes hätte:
a && b || c || d && e
Ok, unter Berücksichtigung der höheren Priorität erhalten wir folgendes: ((a && b) || c) || (d && e)
beachte, dass ich mir nicht 100% sicher bin, dass entweder die Klammer c mit der ersten && oder der letzten setzen würde, aber es spielt keine Rolle, die Logik ist die gleiche Wenn man den Baum visualisiert, beginne mit der innersten Klammern. Sie sind die "Blätter", die dann nach außen arbeiten, indem sie die Klammern verwenden, um den Baum zu bearbeiten, und schließlich am Wurzelknoten ankommen, der in unserem Fall das || ist rechts von c:
||
/ \
|| &&
/ \ / \
&& c d e
/ \
a b