Linq zu SQL-Abfragen von einem nicht-trivialen IQueryable kompilieren

.net expression-trees linq-to-sql performance

Frage

Gibt es eine Möglichkeit, die CompiledQuery.Compile-Methode zu verwenden, um den Ausdruck zu kompilieren, der mit einem IQueryable verknüpft ist? Momentan habe ich ein IQueryable mit einem sehr großen Expression Tree dahinter. Das IQueryable wurde mit verschiedenen Methoden aufgebaut, die jeweils Komponenten liefern. Beispielsweise können zwei Methoden IQueryables zurückgeben, die dann in einem dritten verknüpft werden. Aus diesem Grund kann ich den gesamten Ausdruck innerhalb des Methodenaufrufs compile () nicht explizit definieren.

Ich hatte gehofft, den Ausdruck als someIQueryable.Expression in die Compile-Methode zu übergeben, jedoch hat dieser Ausdruck nicht die Form, die für die Kompiliermethode erforderlich ist. Wenn ich versuche, dies zu umgehen, indem ich die Abfrage direkt in die Compile-Methode lege, zB:

    var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(dc => dc.getUsers());
    var bar = foo(this);

wo ich das Anrufformular innerhalb eines Datenkontexts mache, erhalte ich einen Fehler, der besagt, dass "getUsers nicht als gespeicherte Prozedur oder benutzerdefinierte Funktion zugeordnet ist". Wieder kann ich nicht einfach den Inhalt der getUsers-Methode dorthin kopieren, wo ich den Compiler-Aufruf mache, da dieser wiederum andere Methoden verwendet.

Gibt es eine Möglichkeit, den Ausdruck auf dem von "getUsers" zurückgegebenen IQueryable in die Compile-Methode zu übergeben?

Aktualisiere Ich habe versucht , meinen Willen auf dem System mit dem folgenden Code zu erzwingen:

    var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(dc => dc.getUsers());
    var bar = foo(this);

foo endet als:

{System.Data.Linq.SqlClient.SqlProvider + OneTimeEnumerable`1 [Model.Entities.User]}

Ich habe keine Möglichkeit, die Ergebnisse in foo zu sehen, da ich nicht nur die Ergebnisse erweitern und die Abfrage ausführen kann, sondern nur die Meldung "Operation könnte die Laufzeit destabilisieren".

Ich muss nur einen Weg finden, damit die SQL-Zeichenfolge nur einmal generiert und als parameterisierter Befehl für nachfolgende Anforderungen verwendet wird. Ich kann dies manuell mit der GetCommand-Methode für den Datenkontext tun, muss dann aber explizit alle Parameter festlegen und mache das Objekt-Mapping selbst, was angesichts der Komplexität dieser speziellen Abfrage ein paar hundert Zeilen Code ist.

Aktualisiert

John Rusk lieferte die nützlichsten Informationen, also vergab ich ihm den Sieg. Es waren jedoch einige zusätzliche Feinabstimmungen erforderlich und es gab einige andere Probleme, denen ich auf dem Weg begegnete, so dass ich dachte, dass ich auf die Antwort "erweitern" würde. Erstens, der Fehler "Operation könnte den Laufzeitfehler destabilisieren" war nicht auf die Kompilierung des Ausdrucks zurückzuführen, sondern auf ein tiefes Casting im Ausdrucksbaum. An einigen Stellen musste ich die .Cast<T>() -Methode aufrufen, um Elemente formell zu übertragen, selbst wenn sie vom richtigen Typ waren. Ohne zu sehr ins Detail zu gehen, war dies im Grunde erforderlich, wenn mehrere Ausdrücke in einem einzigen Baum zusammengefasst wurden und jeder Zweig einen anderen Typ zurückgeben konnte, die jeweils der Untertyp einer gemeinsamen Klasse waren.

Nachdem ich das Destabilisierungsproblem gelöst hatte, kehrte ich zum Kompilierungsproblem zurück. Johns Expand-Lösung war fast da. Es suchte nach Methodenaufrufausdrücken in der Struktur und versuchte, sie in den zugrunde liegenden Ausdruck aufzulösen, den diese Methode normalerweise zurückgeben würde. Meine Verweise auf Ausdrücke wurden nicht durch Methodenaufrufe bereitgestellt, sondern durch Eigenschaften. Daher musste ich den Ausdruck visitor ändern, der die Erweiterung ausführt, um diese Typen einzubeziehen:

    var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(dc => dc.getUsers());
    var bar = foo(this);

Diese Methode ist möglicherweise nicht in allen Fällen angebracht, aber sie sollte jedem helfen, der sich in derselben misslichen Lage befindet.

Akzeptierte Antwort

So etwas funktioniert zumindest in meinen Tests:

Expression<Func<DataContext, IQueryable<User>> queryableExpression = GetUsers();
var expressionWithSomeAddedStuff = (DataContext dc) => from u in queryableExpression.Invoke(dc) where ....;
var expressionThatCanBeCompiled = expressionWithSomeAddedStuff.Expand();
var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(expressionThatCanBeCompiled);

Das sieht ein bisschen aus, und es gibt wahrscheinlich Verbesserungen, die Sie machen können.

Der Schlüsselpunkt ist, dass es die Methoden Invoke und Expand von LinqKit verwendet. Sie lassen Sie im Grunde eine Abfrage erstellen, durch Komposition, und kompilieren Sie dann das fertige Ergebnis.




Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum