Gibt es eine Möglichkeit, die CompiledQuery.Compile-Methode zu verwenden, um den Ausdruck zu kompilieren, der mit einer IQueryable verbunden ist? Derzeit habe ich eine IQueryable mit einem sehr großen Expression-Baum hinter ihm. Die IQueryable wurde mit mehreren Methoden aufgebaut, die jeweils Komponenten liefern. Beispielsweise können zwei Methoden IQueryables zurückgeben, die dann in einer dritten Methode zusammengeführt werden. Aus diesem Grund kann ich den gesamten Ausdruck nicht explizit innerhalb des Aufrufs der Methode compile() definieren.
Ich hatte gehofft, den Ausdruck in die Kompiliermethode als someIQueryable.Expression zu übergeben, aber dieser Ausdruck ist nicht in der Form, die von der Kompiliermethode erforderlich. Wenn ich versuche, dies zu umgehen, indem ich die Abfrage direkt in die Kompiliermethode einfüge, z. B:
var foo = CompiledQuery.Compile<DataContext, IQueryable<User>>(dc => dc.getUsers());
var bar = foo(this);
wo ich den Aufruf aus einem Datentext heraus tätige, erhalte ich eine Fehlermeldung, die besagt, dass "getUsers nicht als gespeicherte Prozedur oder benutzerdefinierte Funktion abgebildet ist". Auch hier kann ich nicht einfach den Inhalt der getUsers-Methode dorthin kopieren, wo ich den Kompilierungsaufruf tätige, da dieser wiederum andere Methoden verwendet.
Gibt es eine Möglichkeit, die Expression auf die IQueryable zurückgegeben von "getUsers" in die Compile-Methode übergeben?
Aktualisiert Ich habe versucht, dem System mit folgendem Code meinen Willen aufzuzwingen:
var phony = Expression.Lambda<Func<DataContext, IQueryable<User>>>(
getUsers().Expression, Expression.Parameter(typeof(DataContext), "dc"));
Func<DataContext, IQueryable<User>> wishful = CompiledQuery.Compile<DataContext, IQueryable<User>>(phony);
var foo = wishful(this);
foo endet:
{System.Data.Linq.SqlClient.SqlProvider+OneTimeEnumerable`1[Model.Entities.User]}
Ich habe nicht die Möglichkeit, die Ergebnisse in foo zu sehen, da ich statt des Angebots, die Ergebnisse zu erweitern und die Abfrage auszuführen, nur die Meldung "Operation could destabilize the runtime" sehe.
Ich muss nur einen Weg für die SQL-Zeichenfolge zu finden, nur einmal generiert werden und als paramaterized Befehl auf nachfolgende Anforderungen verwendet werden, kann ich dies manuell mit der GetCommand-Methode auf den Datenkontext zu tun, aber dann muss ich explizit alle Parameter festgelegt und tun die Objektzuordnung selbst, die ein paar hundert Zeilen Code angesichts der Komplexität dieser bestimmten Abfrage ist.
Aktualisiert
John Rusk lieferte die nützlichsten Informationen, so dass ich ihm in diesem Fall den Sieg zusprach. Es waren jedoch einige zusätzliche Anpassungen erforderlich, und es gab noch einige andere Probleme, auf die ich unterwegs gestoßen bin, so dass ich die Antwort erweitern möchte. Erstens war der Fehler "Operation could destabalize the runtime" nicht auf die Kompilierung des Ausdrucks zurückzuführen, sondern auf ein Casting tief im Ausdrucksbaum. An einigen Stellen musste ich die .Cast<T>()
Methode, um Gegenstände förmlich zu werfen, selbst wenn sie vom richtigen Typ waren. Ohne zu sehr ins Detail zu gehen, war dies grundsätzlich erforderlich, wenn mehrere Ausdrücke in einem einzigen Baum kombiniert wurden und jeder Zweig einen anderen Typ zurückgeben konnte, der jeweils der Untertyp einer gemeinsamen Klasse war.
Nachdem ich das Problem der Destabilisierung gelöst hatte, kehrte ich zu dem Kompilierungsproblem zurück. Johns Expand-Lösung war fast fertig. Sie suchte nach Ausdrücken für Methodenaufrufe in der Baumstruktur und versuchte, sie in den zugrunde liegenden Ausdruck aufzulösen, den die Methode normalerweise zurückgeben würde. Meine Verweise auf Ausdrücke wurden nicht durch Methodenaufrufe bereitgestellt, sondern durch Eigenschaften. Ich musste also den Expression Visitor, der die Expansion durchführt, so ändern, dass er diese Typen einschließt:
protected override Expression VisitMemberAccess(MemberExpression m) {
if(m.Method.DeclaringType == typeof(ExpressionExtensions)) {
return new ExpressionExpander().Visit((Expression)(((System.Reflection.PropertyInfo)m.Member).GetValue(null, null)));
}
return base.VisitMemberAccess(m);
}
Diese Methode ist vielleicht nicht in allen Fällen geeignet, aber sie sollte jedem helfen, der sich in der gleichen Lage befindet.