Wie erstelle ich eine Ausdrucksbaumstruktur, die IEnumerable & lt; TSource & gt; .Any (...) aufruft?
Ich versuche, erstellen Sie einen Ausdruck-Baum, stellt die folgende:
myObject.childObjectCollection.Any(i => i.Name == "name");
Verkürzt aus Gründen der übersichtlichkeit habe ich den folgenden:
//'myObject.childObjectCollection' is represented here by 'propertyExp'
//'i => i.Name == "name"' is represented here by 'predicateExp'
//but I am struggling with the Any() method reference - if I make the parent method
//non-generic Expression.Call() fails but, as per below, if i use <T> the
//MethodInfo object is always null - I can't get a reference to it
private static MethodCallExpression GetAnyExpression<T>(MemberExpression propertyExp, Expression predicateExp)
{
MethodInfo method = typeof(Enumerable).GetMethod("Any", new[]{ typeof(Func<IEnumerable<T>, Boolean>)});
return Expression.Call(propertyExp, method, predicateExp);
}
Was mache ich falsch? Jemand irgendwelche Vorschläge?
InformationsquelleAutor der Frage flesh | 2008-11-28
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es gibt mehrere Dinge falsch mit, wie Sie gehen über es.
Sie mischen Abstraktionsebenen. Der T-parameter, um
GetAnyExpression<T>
anders sein könnte, um den Typ-parameter zu instanziierenpropertyExp.Type
. Die T-Typ-parameter ist ein Schritt näher in die Abstraktion stack zu kompilieren - es sei denn, Sie fordernGetAnyExpression<T>
über Reflexion, es wird bestimmt werden zur compile-Zeit - aber die Art, eingebettet in die Ausdruck vergangen, alspropertyExp
erst zur Laufzeit bestimmt wird. Ihre Weitergabe, für das Prädikat alsExpression
ist auch eine Abstraktion, die mixup - das ist der nächste Punkt.Dem Prädikat Sie auf der Durchreise sind
GetAnyExpression
sollte ein Delegierter Wert, nicht eineExpression
jeglicher Art, da Sie versuchen, rufen SieEnumerable.Any<T>
. Wenn Sie versuchen, rufen Sie einen Ausdruck-Baum-version vonAny
ist, dann solltest du-pass einLambdaExpression
statt, die Sie zitieren, und ist einer der seltenen Fälle, in denen Sie gerechtfertigt sein könnte, dass man eine spezifischere Art, als Ausdruck, das führt mich zu meinem nächsten Punkt.In der Regel sollten Sie vorbeikommen
Expression
Werte. Beim arbeiten mit expression trees im Allgemeinen - und dies gilt für alle Arten von Compilern, nicht nur LINQ und seine Freunde - das sollten Sie tun dies in einer Weise, dass der Agnostiker als auf die unmittelbare Zusammensetzung der Knoten der Baumstruktur, die Sie gerade arbeiten. Sie sind vorausgesetztdass Sie anrufenAny
auf eineMemberExpression
aber nicht tatsächlich wissen müssendass Sie ' re Umgang mit einemMemberExpression
nur einExpression
Typ einige InstanziierungIEnumerable<>
. Dies ist ein häufiger Fehler bei Menschen, die nicht vertraut mit den Grundlagen der compiler ASTs. Frans Bouma immer wieder den gleichen Fehler gemacht, als er anfing zu arbeiten mit expression trees - denken in besonderen Fällen. Denke im Allgemeinen. Sie sparen sich eine Menge ärger in der Mittel-und langfristig.Und hier kommt das Fleisch für dein problem (obwohl der zweite und wohl eine der ersten Fragen wäre immerhin, wenn Sie bekommen hatte, an ihm vorbei) - Sie müssen feststellen, dass die entsprechende generische überladung der Methode, und dann instanziieren es mit dem richtigen Typ. Reflexion nicht bieten Ihnen mit einer einfach hier; Sie brauchen, zu Durchlaufen und finden eine entsprechende version.
So, es brechen: Sie müssen zu finden eine generische Methode (
Any
). Hier ist eine utility-Funktion, die das tut:Allerdings erfordert es die Art Argumente und das richtige argument-Typen. Bekommen, dass von Ihrem
propertyExp
Expression
ist nicht ganz trivial, weil dieExpression
kannList<T>
geben, oder eine andere Art, aber wir müssen feststellen, dass dieIEnumerable<T>
Instanziierung und Holen Sie deren Typ-argument. Ich habe gekapselt, dass in ein paar Funktionen:So, da jeder
Type
wir können nun ziehen dieIEnumerable<T>
Instanziierung aus der it - und geltend zu machen, wenn es nicht (genau).Mit dieser Arbeit aus dem Weg, lösen das eigentliche problem ist nicht allzu schwer. Ich habe umbenannt, Ihre Methode zu CallAny, und verändert die parameter-Typen vorgeschlagen:
Hier ein
Main()
- routine, die verwendet alle oben genannten code und überprüft, ob es funktioniert für einen trivialen Fall:InformationsquelleAutor der Antwort Barry Kelly
Barry ' s Antwort stellt eine funktionierende Lösung zu der Frage des original-Posters. Vielen Dank an diese beiden Personen für das stellen und beantworten.
Ich diesen thread gefunden, als ich versuchte, erarbeiten eine Lösung für ein ganz ähnliches problem: programmgesteuert erstellen Sie einen Ausdruck-Baum, enthält einen Aufruf auf den Any () - Methode. Als eine weitere Einschränkung, die allerdings die ultimative Ziel meine Lösung war, passieren solche einem dynamisch erstellten Ausdruck durch Linq-to-SQL, so dass die Arbeit der () - Auswertung tatsächlich ausgeführt in der DB selbst.
Leider die Lösung als bisher besprochen haben, ist nicht etwas, dass Linq-to-SQL verarbeiten kann.
Betrieb unter der Annahme, dass dies vielleicht ein ziemlich beliebtes Grund für den Wunsch nach Aufbau einer dynamischen Ausdruck Baum, habe ich beschlossen, zu erweitern, den thread mit meinen Ergebnissen.
Als ich versuchte zu verwenden, das Ergebnis von Barry ' s CallAny() als Ausdruck in einer Linq-to-SQL-Where () - Klausel, erhielt ich eine InvalidOperationException mit einer der folgenden Eigenschaften:
Nach einem Vergleich eine hart-kodierte expression tree der dynamisch-erstellt mit CallAny(), fand ich, dass der Kern des Problems war aufgrund der Compile () - Prädikat-Ausdruck und der Versuch zum aufrufen der resultierende Delegat in der CallAny(). Ohne zu Graben tief in Linq-to-SQL-Implementierung-details, schien es mir vernünftig, dass Linq-to-SQL würde nicht wissen, was zu tun mit solch einer Struktur.
Daher, nach einigem Experimentieren, konnte ich das erreichen meiner gewünschten Ziel, indem Sie Sie leicht überarbeiten der vorgeschlagenen CallAny() implementation einer predicateExpression eher als ein Delegierter für Jedes () - Prädikat-Logik.
Meine überarbeitete Methode ist:
Nun werde ich zeigen, die seine Verwendung mit EF. Für Klarheit sollte ich zuerst zeigen die Spielzeug-domain-model & EF Kontext ich bin mit. Im Grunde ist mein Modell ist eine vereinfachende Blogs & Beiträge-domain ... wo ein blog hat mehrere Beiträge und jeder Beitrag hat ein Datum:
Mit dieser domain eingerichtet, hier ist mein code, um letztlich die Ausübung des überarbeiteten CallAny() und Linq-to-SQL die Arbeit der Auswertung, die Alle(). Meine besonderen Beispiel wird der Fokus auf die RÜCKFÜHRUNG aller Blogs die mindestens einen Beitrag, der neuer ist als eine bestimmte cutoff-Datum.
Wo BuildExpressionForBlogsWithRecentposts() ist eine Hilfsfunktion, die verwendet CallAny() wie folgt:
HINWEIS: ich fand eine andere, scheinbar unwichtige delta zwischen hard-codiert und dynamisch-integrierte Ausdrücke. Die dynamisch gebaut, hat man einen "extra" konvertieren rufen, dass es in der hart codierte version nicht zu haben scheinen (oder müssen?). Die Umwandlung eingeführt wird, in der CallAny () - Implementierung. Linq-to-SQL scheint ok zu sein, so dass ich es gelassen (obwohl es unnötig war). Ich war nicht ganz sicher, ob diese Umstellung vielleicht notwendig sein, einige robustere Nutzungen als mein Spielzeug Probe.
InformationsquelleAutor der Antwort Aaron Heusser