2010-12-08 9 views
7

Angesichts der folgenden LINQ-Anweisung (en), die effizienter sein wird?LINQ ToList(). Take (10) vs Take (10) .ToList() welche erzeugt effizientere Abfrage

ONE:

public List<Log> GetLatestLogEntries() 
{ 
    var logEntries = from entry in db.Logs 
       select entry; 
    return logEntries.ToList().Take(10); 
} 

ZWEI:

public List<Log> GetLatestLogEntries() 
{ 
    var logEntries = from entry in db.Logs 
       select entry; 
    return logEntries.Take(10).ToList(); 
} 

Ich bin mir bewusst, dass .ToList() sofort die Abfrage ausführt.

Antwort

17

Die erste Version würde nicht einmal kompilieren - weil der Rückgabewert von Take ein IEnumerable<T>, keine List<T>. So sollten Sie es brauchen:

public List<Log> GetLatestLogEntries() 
{ 
    var logEntries = from entry in db.Logs 
       select entry; 
    return logEntries.ToList().Take(10).ToList(); 
} 

, dass alle Daten aus der Datenbank holen würde und es in eine Liste konvertieren, dann nehmen die ersten 10 Einträge, dann wandeln es wieder in eine Liste .

die Take(10) Erste in der Datenbank auftreten (dh die zweite Form) sicherlich ein Heck von viel billiger für mich sieht ...

Hinweis, dass es keine Queryable.ToList() Methode - Sie werden Enumerable.ToList() Aufruf am Ende der ruft alle Einträge ab. Mit anderen Worten, der Aufruf an ToListbeteiligt sich nicht an SQL-Übersetzung, während Take tut.

Beachten Sie auch, dass die Verwendung eines Abfrageausdrucks auch hier keinen Sinn ergibt. Ich würde schreiben Sie es als:

public List<Log> GetLatestLogEntries() 
{ 
    return db.Log.Take(10).ToList(); 
} 

Wohlgemerkt, erhalten Sie einen Anruf OrderBy möchten - sonst werde es nehmen nur die ersten 10 Einträge es findet, die nicht dem neuesten Stand sind sein kann ...

+0

Danke, Jon, dafür. Sehr hilfreich. –

2

Die zweite Version wird effizienter sein (sowohl bei der Zeit als auch bei der Speichernutzung). Zum Beispiel vorstellen, dass Sie eine Sequenz enthält 1.000.000 Einheiten:

  1. Die erste Version durchläuft alle 1.000.000 Artikel und fügte hinzu, sie zu einer Liste, wie es geht. Dann werden schließlich die ersten 10 Elemente aus dieser großen Liste benötigt.

  2. Die zweite Version muss nur die ersten 10 Elemente durchlaufen und fügt sie zu einer Liste hinzu, wie es geht. (Die restlichen 999.990 Produkte brauchen nicht einmal in Betracht gezogen werden.)

2

Ihre erste Option wird nicht funktionieren, weil .Take(10) es IEnumerable<Log> umwandelt. Ihr Rückgabetyp ist List<Log>, also müssten Sie return logEntries.ToList().Take(10).ToList() tun, was ineffizienter ist.

Indem Sie .ToList().Take(10) tun, erzwingen Sie die .Take(10) LINQ zu Objekten, während die andere Möglichkeit der Filter an die Datenbank oder andere zugrunde liegende Datenquelle übergeben werden konnte. Mit anderen Worten, wenn Sie zuerst .ToList() machen, müssen ALLE Objekte von der Datenbank übertragen und im Speicher zugeordnet werden. Dann filtern Sie auf die ersten 10. Wenn Sie von Millionen von Datenbankzeilen (und Objekten) sprechen, können Sie sich vorstellen, dass dies SEHR ineffizient und nicht skalierbar ist.

Die zweite wird auch sofort laufen, weil Sie .ToList() haben, also keinen Unterschied gibt.

0

Die zweite Option.

Die erste wird die gesamte Aufzählung bewerten, schlürfen es in eine List(); dann richten Sie den Iterator ein, der die ersten zehn Objekte durchlaufen und dann beenden wird.

Die zweite setzt den Take() - Iterator zuerst, also was auch immer später passiert, nur 10 Objekte werden ausgewertet und an die "Downstream" -Verarbeitung gesendet (in diesem Fall die ToList(), die diese zehn Elemente nimmt) und geben Sie sie als die konkrete Liste zurück).

1

Wie wäre es damit?

I have 5000 records in "items" 

Version 1:

IQueryable<T> items = Items; // my items 
    items = ApplyFilteringCriteria(items, filter); // my filter BL 
    items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL 
    items = items.Skip(0); 
    items = items.Take(25); 
    return items.ToList(); 

dies geschah: 20 sec auf Server

Version 2:

IQueryable<T> items = Items; // my items 
    items = ApplyFilteringCriteria(items, filter); // my filter BL 
    items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL 
    List<T> x = items.ToList(); 
    items = x.Skip(0).ToList(); 
    items = x.Take(25).ToList(); 
    return x; 

dies geschah: 1 sec auf Server

Was Denkst du jetzt? Irgendeine Idee warum?

+0

Version 1 ist Iqueryable, dh es wird Ihre Datenbank abfragen. Linq ist damit nicht immer so effizient. Version 2 wird in eine Liste eingefügt, dh ab diesem Zeitpunkt werden keine Abfragen mehr auf die Datenbank gesendet. –