Ich kenne einige Unterschiede zwischen LINQ zu Entitäten und LINQ zu Objekten, die der erste IQueryable
und der zweite implementiert, IEnumerable
und mein Fragenbereich liegt innerhalb von EF 5.
Meine Frage ist, was ist der technische Unterschied (die technischen Unterschiede) dieser drei Methoden? Ich sehe, dass in vielen Situationen alle funktionieren. Ich sehe auch Kombinationen davon gerne .ToList().AsQueryable()
.
Was bedeuten diese Methoden genau?
Gibt es ein Leistungsproblem oder etwas, das dazu führen würde, dass eines über das andere verwendet wird?
Warum sollte man zum Beispiel
.ToList().AsQueryable()
statt verwenden.AsQueryable()
?
Antworten:
Dazu gibt es viel zu sagen. Lassen Sie mich auf
AsEnumerable
und konzentrierenAsQueryable
undToList()
auf dem Weg erwähnen .Was machen diese Methoden?
AsEnumerable
undAsQueryable
gegossen oder konvertiert zuIEnumerable
oderIQueryable
. Ich sage Besetzung oder Konvertierung mit einem Grund:Wenn das Quellobjekt bereits den Ziel - Schnittstelle implementiert, wird das Quellobjekt selbst zurückgegeben , sondern warf auf die Zieloberfläche. Mit anderen Worten: Der Typ wird nicht geändert, der Typ zur Kompilierungszeit jedoch.
Wenn das Quellobjekt die Zielschnittstelle nicht implementiert, wird das Quellobjekt in ein Objekt konvertiert , das die Zielschnittstelle implementiert. Daher werden sowohl der Typ als auch der Typ zur Kompilierungszeit geändert.
Lassen Sie mich dies anhand einiger Beispiele zeigen. Ich habe diese kleine Methode, die den Typ der Kompilierungszeit und den tatsächlichen Typ eines Objekts angibt (mit freundlicher Genehmigung von Jon Skeet ):
Versuchen wir es mit einer beliebigen Linq-to-SQL-Methode
Table<T>
, die Folgendes implementiertIQueryable
:Das Ergebnis:
Sie sehen, dass die Tabellenklasse selbst immer zurückgegeben wird, ihre Darstellung sich jedoch ändert.
Nun ein Objekt, das implementiert
IEnumerable
, nichtIQueryable
:Die Ergebnisse:
Da ist es.
AsQueryable()
hat das Array in ein Array konvertiertEnumerableQuery
, das "eineIEnumerable<T>
Sammlung alsIQueryable<T>
Datenquelle darstellt". (MSDN).Was ist der Nutzen?
AsEnumerable
wird häufig verwendet, um von einerIQueryable
Implementierung zu LINQ zu Objekten (L2O) zu wechseln , hauptsächlich weil ersteres keine Funktionen unterstützt, über die L2O verfügt. Weitere Informationen finden Sie unter Welche Auswirkungen hat AsEnumerable () auf eine LINQ-Entität? .In einer Entity Framework-Abfrage können wir beispielsweise nur eine begrenzte Anzahl von Methoden verwenden. Wenn wir zum Beispiel eine unserer eigenen Methoden in einer Abfrage verwenden müssen, schreiben wir normalerweise so etwas wie
ToList
- was einIEnumerable<T>
in ein umwandeltList<T>
- wird häufig auch für diesen Zweck verwendet. Der Vorteil der Verwendung vonAsEnumerable
vs.ToList
besteht darin, dassAsEnumerable
die Abfrage nicht ausgeführt wird.AsEnumerable
Bewahrt die verzögerte Ausführung und erstellt keine häufig nutzlose Zwischenliste.Wenn andererseits eine erzwungene Ausführung einer LINQ-Abfrage gewünscht wird,
ToList
kann dies eine Möglichkeit sein.AsQueryable
kann verwendet werden, um eine aufzählbare Sammlung Ausdrücke in LINQ-Anweisungen akzeptieren zu lassen. Weitere Informationen finden Sie hier: Muss ich AsQueryable () wirklich für die Sammlung verwenden? .Hinweis zum Drogenmissbrauch!
AsEnumerable
wirkt wie eine Droge. Es ist eine schnelle Lösung, aber kostenpflichtig und behebt das zugrunde liegende Problem nicht.In vielen Antworten zum Stapelüberlauf sehe ich Leute, die sich bewerben
AsEnumerable
, um fast jedes Problem mit nicht unterstützten Methoden in LINQ-Ausdrücken zu beheben. Der Preis ist aber nicht immer klar. Wenn Sie dies beispielsweise tun:... alles wird ordentlich in eine SQL-Anweisung übersetzt, die filter (
Where
) und projects (Select
). Das heißt, sowohl die Länge als auch die Breite der SQL-Ergebnismenge werden reduziert.Angenommen, Benutzer möchten nur den Datumsteil von sehen
CreateDate
. In Entity Framework werden Sie schnell feststellen, dass ...... wird nicht unterstützt (zum Zeitpunkt des Schreibens). Ah, zum Glück gibt es die
AsEnumerable
Lösung:Sicher, es läuft wahrscheinlich. Es zieht jedoch die gesamte Tabelle in den Speicher und wendet dann den Filter und die Projektionen an. Nun, die meisten Leute sind klug genug, um das
Where
erste zu tun :Trotzdem werden zuerst alle Spalten abgerufen und die Projektion erfolgt im Speicher.
Die eigentliche Lösung ist:
(Aber das erfordert nur ein bisschen mehr Wissen ...)
Was machen diese Methoden NICHT?
Stellen Sie die IQueryable-Funktionen wieder her
Nun eine wichtige Einschränkung. Wenn Sie das tun
Sie erhalten das Quellobjekt, das als dargestellt wird
IQueryable
. (Weil beide Methoden nur umwandeln und nicht konvertieren).Aber wenn du es tust
Was wird das Ergebnis sein?
Das
Select
produziert aWhereSelectEnumerableIterator
. Dies ist eine interne .NET - Klasse, die implementiertIEnumerable
, nichtIQueryable
. Es wurde also eine Konvertierung in einen anderen Typ durchgeführt, und die nachfolgendeAsQueryable
kann die ursprüngliche Quelle nie mehr zurückgeben.Die Folge davon ist , dass die Verwendung
AsQueryable
ist nicht ein Weg , um auf magische Weise inject ein Abfrage - Provider mit seinem spezifischen Funktionen in eine zählbaren. Angenommen, Sie tun esDie where-Bedingung wird niemals in SQL übersetzt.
AsEnumerable()
gefolgt von LINQ-Anweisungen wird die Verbindung zum Entity Framework-Abfrageanbieter endgültig unterbrochen.Ich zeige dieses Beispiel absichtlich, weil ich hier Fragen gesehen habe, bei denen beispielsweise versucht wird,
Include
Funktionen durch Aufrufen in eine Sammlung einzufügenAsQueryable
. Es wird kompiliert und ausgeführt, tut aber nichts, da das zugrunde liegende Objekt keineInclude
Implementierung mehr hat.Ausführen
Sowohl
AsQueryable
undAsEnumerable
nicht ausführen (oder enumerate ) das Quellobjekt. Sie ändern nur ihren Typ oder ihre Darstellung. Beide beteiligten SchnittstellenIQueryable
undIEnumerable
sind nichts anderes als "eine Aufzählung, die darauf wartet, passiert zu werden". Sie werden nicht ausgeführt, bevor sie dazu gezwungen werden, beispielsweise wie oben erwähnt, durch AufrufenToList()
.Das bedeutet, dass beim Ausführen eines
IEnumerable
durch AufrufenAsEnumerable
einesIQueryable
Objekts erhaltenen Objekts der Basiswert ausgeführt wirdIQueryable
. Eine nachfolgende Ausführung desIEnumerable
Testaments führt das erneut ausIQueryable
. Welches kann sehr teuer sein.Spezifische Implementierungen
Bisher ging es nur um die
Queryable.AsQueryable
undEnumerable.AsEnumerable
Erweiterungsmethoden. Aber natürlich kann jeder Instanzmethoden oder Erweiterungsmethoden mit denselben Namen (und Funktionen) schreiben.In der Tat ist ein häufiges Beispiel für eine bestimmte
AsEnumerable
ErweiterungsmethodeDataTableExtensions.AsEnumerable
.DataTable
implementiert nichtIQueryable
oderIEnumerable
, daher gelten die regulären Erweiterungsmethoden nicht.quelle
AsQueryable()
basiert die Bewerbung häufig auf falschen Vorstellungen. Aber ich lasse es für eine Weile in meinem Hinterkopf köcheln und sehe, ob ich diese Frage noch ausführlicher behandeln kann.Auflisten()
AsEnumerable ()
Func<TSource, bool>
AsQueryable ()
Expression<Func<TSource, bool>>
AsQueryable()
normalerweise viel schneller alsAsEnumerable()
beim erstmaligen Generieren von T-SQL, das alle Ihre Where-Bedingungen in Ihrem Linq enthält.quelle
ToList () ist alles im Speicher und dann werden Sie daran arbeiten. Daher wird ToList (). where (Filter anwenden) lokal ausgeführt. AsQueryable () führt alles remote aus, dh ein Filter wird zur Anwendung an die Datenbank gesendet. Queryable macht nichts, bis Sie es ausführen. ToList wird jedoch sofort ausgeführt.
Schauen Sie sich auch diese Antwort an. Warum sollten Sie AsQueryable () anstelle von List () verwenden? .
BEARBEITEN: In Ihrem Fall ist jede nachfolgende Operation lokal, einschließlich AsQueryable (), sobald Sie ToList () ausführen. Sie können nicht zu Remote wechseln, sobald Sie mit der lokalen Ausführung beginnen. Hoffe das macht es ein bisschen klarer.
quelle
Beim folgenden Code ist eine schlechte Leistung aufgetreten.
Behoben mit
Bleiben Sie für ein IQueryable nach Möglichkeit in IQueryable und versuchen Sie, nicht wie IEnumerable verwendet zu werden.
Update . Dank Gert Arnold kann es in einem Ausdruck weiter vereinfacht werden .
quelle