IEnumerable<T> vs IQueryable<T>: Was in C# wählen? Vollständige Analyse

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

IEnumerable<T> vs IQueryable<T>: Ein Duell, das über das Schicksal Ihrer Anwendung entscheidet

\n\n

Haben Sie sich jemals gefragt, warum Ihre Datenbankabfrage lahmt, obwohl die Datenmenge überschaubar ist? Oder warum Ihre Anwendung nach dem Hinzufügen einer einzigen Codezeile zur Schnecke wird? Höchstwahrscheinlich sind Sie auf das klassische Dilemma gestoßen: IEnumerable<T> oder IQueryable<T>? Dies sind nicht nur zwei Schnittstellen aus der Welt von C# und LINQ. Es sind zwei verschiedene Welten, zwei Ansätze zur Datenverarbeitung. Eine für lokale Sammlungen, die andere für entfernte Quellen wie Datenbanken. In diesem Artikel werden wir nicht nur die Unterschiede auflisten, sondern sie anhand realer Beispiele analysieren, damit Sie nie wieder in eine Leistungsfalle tappen.

\n\n

Wer ist wer: Grundlegende Konzepte

\n\n

Beginnen wir mit den Grundlagen. IEnumerable<T> ist eine Schnittstelle, die eine Sequenz von Elementen im Arbeitsspeicher darstellt. Sie eignet sich ideal für die Arbeit mit Sammlungen wie List<T>, Array oder Dictionary. Wenn Sie IEnumerable verwenden, werden alle Operationen (Filtern, Sortieren) clientseitig, also in Ihrer Anwendung, ausgeführt.

\n\n

IQueryable<T> ist eine fortschrittlichere Schnittstelle, die für die Arbeit mit externen Datenquellen (Datenbanken, Webservices) konzipiert ist. Sie speichert keine Daten, sondern erstellt einen Ausdrucksbaum (Expression Tree). Dies ermöglicht es dem LINQ-Anbieter (z. B. Entity Framework), Ihren C#-Code in eine effiziente SQL-Abfrage umzuwandeln und diese serverseitig auszuführen.

\n\n

Der Schlüsselsatz, den Sie sich merken sollten: IEnumerable arbeitet im Arbeitsspeicher, IQueryable auf der Seite der Datenquelle.

\n\n

Der Hauptunterschied: Wo und wann die Logik ausgeführt wird

\n\n

Stellen Sie sich vor, Sie müssen eine Liste von 10.000 Produkten filtern, die teurer als 1000 Rubel sind, und sie nach Preis sortieren.

\n\n

Beispiel 1: IEnumerable (Filterung im Arbeitsspeicher)

\n
// Angenommen, products ist List<Product> (IEnumerable)\nvar expensiveProducts = products\n    .Where(p => p.Price > 1000)\n    .OrderBy(p => p.Price)\n    .ToList();\n
\n

Was passiert hier? Zuerst werden ALLE 10.000 Produkte in den Arbeitsspeicher geladen. Dann wendet C# den Filter Where und die Sortierung OrderBy in Ihrer Anwendung an. Bei 10.000 Datensätzen ist das nicht kritisch. Aber bei 10 Millionen würde die Anwendung abstürzen oder einfrieren.

\n\n

Beispiel 2: IQueryable (Filterung auf dem Datenbankserver)

\n
// Angenommen, dbContext.Products ist DbSet<Product> (IQueryable)\nvar expensiveProducts = dbContext.Products\n    .Where(p => p.Price > 1000)\n    .OrderBy(p => p.Price)\n    .ToList();\n
\n

Hier geschieht die Magie: IQueryable lädt die Daten nicht sofort. Stattdessen erstellt es eine SQL-Abfrage: SELECT * FROM Products WHERE Price > 1000 ORDER BY Price. Diese Abfrage wird an die Datenbank gesendet, und diese gibt nur die gefilterten und sortierten Daten zurück. Im Arbeitsspeicher erhalten Sie nur das, was Sie benötigen. Die Ressourceneinsparung ist enorm.

\n\n

Praktisches Beispiel: Die Falle mit ToList()

\n\n

Einer der häufigsten Fehler von Anfängern ist der verfrühte Aufruf von ToList() oder ToArray(). Dies verwandelt IQueryable in IEnumerable.

\n\n

Schlechter Code (tötet die Leistung):

\n
var allProducts = dbContext.Products.ToList(); // Lädt ALLE Daten in den Speicher!\nvar filteredProducts = allProducts.Where(p => p.Price > 1000).ToList();\n
\n

In diesem Beispiel laden wir zuerst die gesamte Products-Tabelle in den Speicher und filtern sie dann. Das ist gleichbedeutend damit, das gesamte Lager auf den Parkplatz zu entladen und dann im Haufen nach der richtigen Kiste zu suchen.

\n\n

Guter Code (optimal):

Blogs

Buchempfehlungen