2010-07-22 9 views
42

die folgende LINQ to SQL-Abfrage Gegeben:Verständnis .AsEnumerable() in LINQ to SQL

var test = from i in Imports 
      where i.IsActive 
      select i; 

Die interpretierte SQL-Anweisung lautet:

SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1 

sagen, dass ich eine Aktion in der Auswahl ausführen wollte das kann nicht in SQL konvertiert werden. Es ist mein Verständnis, dass der herkömmliche Weg dies zu tun ist, AsEnumerable() damit zu einem praktikablen Objekt zu konvertieren.

diese aktualisierte Code Gegeben:

var test = from i in Imports.AsEnumerable() 
      where i.IsActive 
      select new 
      { 
       // Make some method call 
      }; 

Und aktualisiert SQL:

SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0] 

Hinweis das Fehlen einer where-Klausel in der SQL-Anweisung ausgeführt.

Bedeutet dies, dass die gesamte Tabelle "Imports" im Speicher zwischengespeichert wird? Wäre das überhaupt eine langsame Leistung, wenn die Tabelle eine große Menge an Datensätzen enthält?

Helfen Sie mir zu verstehen, was hier hinter den Kulissen tatsächlich passiert.

+1

Ausgezeichnete Frage! – fernandoespinosa

+0

Hier finden Sie aktuelle Beispiele aus einer anderen Frage: [1] [1] [link Beschreibung hier eingeben]: http://stackoverflow.com/a/14129116/1792434 – lukaszk

Antwort

33

Der Grund für AsEnumerable ist

AsEnumerable (TSource) (IEnumerable (TSource)) verwendet werden kann zwischen Abfrage Implementierungen zu wählen, wenn eine Sequenz implementiert IEnumerable (T), sondern hat auch ein andere Gruppe von öffentlicher Abfrage Methoden zur Verfügung

Also, wenn Sie das Wo Verfahrens vor dem Aufruf wurden, riefen Sie eine andere Wo Methode aus dem IEnume rable.Where. Die WHERE-Anweisung wurde für LINQ in SQL konvertiert. Das neue WHERE ist das IEnumerable-Element, das IEnumerable verwendet, es aufzählt und die übereinstimmenden Elemente liefert. Was erklärt, warum Sie sehen, dass die verschiedenen SQL generiert werden. Die Tabelle wird vollständig aus der Datenbank übernommen, bevor die Where-Erweiterung in Ihrer zweiten Version des Codes angewendet wird. Dies könnte zu einem ernsthaften Flaschenhals führen, da der gesamte Tisch im Speicher sein muss oder, schlimmer noch, der gesamte Tisch zwischen Servern laufen muss. Erlauben Sie SQL Server, das Wo auszuführen und zu tun, was es am besten tut.

+1

So ist es nach wie vor hat die Ausführung aufgeschoben, oder? Mit der obigen Abfrage als Beispiel wird die gesamte "Imports" -Tabelle nicht im Speicher gespeichert, sondern nur die aktiven, wenn ich etwas wie "test.Dump()" mache. Ist das korrekt? –

+0

@Mike Fielden Nun, die ganze Tabelle wird durch die Erinnerung gehen, wie das Aufzählen. Ich bin mir nicht sicher, ob alle Zeilen gleichzeitig in den Speicher geladen werden. –

+0

@YuriyFaktorovich: Also warum existiert es? Bitte sagen Sie auch den Profis, AsEnumerable zu verwenden. Danke – Unbreakable

1

Ich glaube, das AsEnumerable sagt dem Compiler nur, welche Erweiterungsmethoden verwendet werden sollen (in diesem Fall die, die für IEnumerable und nicht für IQueryable definiert sind). Die Ausführung der Abfrage wird immer noch zurückgestellt, bis Sie ToArray aufrufen oder darauf aufzählen.

+0

Das macht keinen Sinn. AsEnumerable ist eine Methode. Es sagt dem Compiler nichts Besonderes. –

+2

Ja, es ist eine Methode, aber der Rückgabetyp ist Enumerable, daher verwendet der Compiler beim Kompilieren der LINQ-Abfrage verschiedene Methoden (Enumerable). Aus der Dokumentation: Die Methode "AsEnumerable (IEnumerable )" hat keine andere Wirkung, als den Kompilierungstyp der Quelle von einem Typ zu ändern, der IEnumerable in IEnumerable selbst implementiert. http://msdn.microsoft.com/en-us/library/bb335435.aspx – Razvi

6

An der Stelle, an der die Enumeration aufgelistet wird, wird die Datenbank abgefragt und das gesamte Resultset abgerufen.

Eine Teil-und-Teil-Lösung kann der Weg sein. Betrachten

Nehmen wir auch an, dass NonDatabaseConvertableCriterion Feld C von Ergebnis erfordert. Da NonDatabaseConvertableCriterion das tut, was der Name andeutet, muss dies als Enumeration ausgeführt werden.Allerdings betrachten:

var partWay = 
(
    from result in SomeSource 
    where DatabaseConvertableCriterion(result) 
    select new {result.A, result.B, result.C} 
); 
var res = 
(
    from result in partWay.AsEnumerable() 
    where NonDatabaseConvertableCriterion select new {result.A, result.B} 
); 

In diesem Fall, wenn res aufgezählt wird, abgefragt oder anderweitig verwendet werden, so viel Arbeit wie möglich in die Datenbank übergeben werden, die den Auftrag fortzusetzen zurückkehren genug. Unter der Annahme, dass es wirklich unmöglich ist, neu zu schreiben, so dass die gesamte Arbeit an die Datenbank gesendet werden kann, könnte dies ein geeigneter Kompromiss sein.

4

Es gibt drei Implementierungen von AsEnumerable.

DataTableExtensions.AsEnumerable

Verlängert ein DataTable es eine IEnumerable Schnittstelle zu geben, damit Sie Linq gegen die DataTable verwenden können.

Enumerable.AsEnumerable<TSource> und ParallelEnumerable.AsEnumerable<TSource>

AsEnumerable<TSource>(IEnumerable<TSource>) Die Methode hat keine Auswirkung andere als die Compiler-Typ der Quelle von einem Typ zu ändern, dass IEnumerable<T> zu IEnumerable<T> implementiert selbst.

AsEnumerable<TSource>(IEnumerable<TSource>) können wählen zwischen Abfrage-Implementierungen verwendet werden, wenn eine Sequenz IEnumerable<T> implementiert, sondern hat auch einen anderen Satz von öffentlichen Abfragemethoden zur Verfügung. Um zum Beispiel eine generische Klasse Table gegeben, die IEnumerable<T> und hat seine eigenen Methoden wie Where, Select und SelectMany, ein Aufruf an die Öffentlichkeit WhereWhere Methode der Table würde aufrufen implementiert. Ein Table Typ, der eine Datenbanktabelle darstellt, könnte eine Where -Methode haben, die das Prädikatargument als Ausdrucksbaum nimmt und den Baum für die Remoteausführung in SQL konvertiert. Wenn die Remoteausführung nicht erwünscht ist, z. B. weil das Prädikat eine lokale Methode aufruft, kann die Methode AsEnumerable<TSource> verwendet werden, um die benutzerdefinierten Methoden auszublenden und stattdessen die Standardabfrageoperatoren verfügbar zu machen.

Mit anderen Worten.

Wenn ich ein

IQueryable<X> sequence = ...; 

von einem LinqProvider, wie Entity Framework, und ich tue,

sequence.Where(x => SomeUnusualPredicate(x)); 

die Abfrage zusammengesetzt sein wird und auf dem Server ausgeführt. Dies wird zur Laufzeit fehlschlagen, da das EntityFramework nicht weiß, wie man SomeUnusualPredicate in SQL konvertiert.

Wenn ich, dass die Aussage mit Linq auf Objekte anstelle ausgeführt werden soll, das tue ich,

sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x)); 

nun der Server alle Daten angezeigt werden können und die Enumerable.Where von Linq zu Objekten wird anstelle der Abfrage verwendet werden Implementierung des Providers

Es spielt keine Rolle, dass Entity Framework nicht wissen, wie SomeUnusualPredicate zu interpretieren, meine Funktion wird direkt verwendet werden. (Dies kann jedoch ein ineffizienter Ansatz sein, da alle Zeilen vom Server zurückgegeben werden.)