Es wurde bereits viel gesagt, aber auf technischere Weise zurück zu den Wurzeln:
IEnumerable
ist eine Sammlung von Objekten im Speicher, die Sie aufzählen können - eine In-Memory-Sequenz, die das Durchlaufen von Objekten ermöglicht (erleichtert das In- foreach
Loop- Verfahren , obwohl Sie IEnumerator
nur damit arbeiten können). Sie befinden sich in der Erinnerung wie sie ist.
IQueryable
ist ein Ausdrucksbaum , der irgendwann in etwas anderes übersetzt wird und über das Endergebnis aufzählen kann . Ich denke, das ist es, was die meisten Menschen verwirrt.
Sie haben offensichtlich unterschiedliche Konnotationen.
IQueryable
stellt einen Ausdrucksbaum (einfach eine Abfrage) dar, der vom zugrunde liegenden Abfrageanbieter in etwas anderes übersetzt wird, sobald Release-APIs aufgerufen werden, z. B. LINQ-Aggregatfunktionen (Summe, Anzahl usw.) oder ToList [Array, Dictionary,. ..]. Und IQueryable
auch Objekte implementieren IEnumerable
, IEnumerable<T>
so dass , wenn sie eine Abfrage repräsentiert das Ergebnis dieser Abfrage könnte wiederholt werden. Dies bedeutet, dass IQueryable nicht nur Abfragen sein müssen. Der richtige Begriff ist, dass sie Ausdrucksbäume sind .
Wie diese Ausdrücke ausgeführt werden und woran sie sich wenden, hängt von den sogenannten Abfrageanbietern ab (Ausdrucksausführende, an die wir sie denken können).
In der Entity Framework- Welt ( dh dem mystischen zugrunde liegenden Datenquellenanbieter oder dem Abfrageanbieter) werden IQueryable
Ausdrücke in native T-SQL- Abfragen übersetzt. Nhibernate
macht ähnliche Dinge mit ihnen. Sie können Ihr eigenes schreiben, indem Sie den in LINQ ziemlich gut beschriebenen Konzepten folgen : Erstellen eines IQueryable Provider- Links, und Sie möchten möglicherweise eine benutzerdefinierte Abfrage-API für Ihren Product Store Provider-Service.
Im Grunde genommen werden IQueryable
Objekte so lange konstruiert, bis wir sie explizit freigeben und das System anweisen, sie in SQL oder was auch immer umzuschreiben und die Ausführungskette zur Weiterverarbeitung herunterzusenden.
Wie bei einer verzögerten Ausführung ist es eine LINQ
Funktion, das Ausdrucksbaumschema im Speicher zu halten und es nur bei Bedarf in die Ausführung zu senden, wenn bestimmte APIs für die Sequenz aufgerufen werden (dieselbe Anzahl, ToList usw.).
Die ordnungsgemäße Verwendung von beiden hängt stark von den Aufgaben ab, denen Sie für den jeweiligen Fall gegenüberstehen. Für das bekannte Repository-Muster entscheide ich mich persönlich für die Rückgabe IList
, IEnumerable
dh über Listen (Indexer und dergleichen). Daher ist es mein Rat, IQueryable
nur innerhalb von Repositorys und IEnumerable irgendwo anders im Code zu verwenden. Nicht über die Bedenken hinsichtlich der Testbarkeit zu sprechen, IQueryable
die zusammenbrechen und das Prinzip der Trennung von Bedenken ruinieren. Wenn Sie einen Ausdruck aus Repositorys zurückgeben, können Verbraucher mit der Persistenzschicht spielen, wie sie es wünschen.
Eine kleine Ergänzung zum Durcheinander :) (aus einer Diskussion in den Kommentaren)) Keines von ihnen ist ein Objekt im Speicher, da es sich nicht um echte Typen an sich handelt, sondern um Marker eines Typs - wenn Sie so tief gehen möchten. Aber es ist sinnvoll (und deshalb hat es sogar MSDN so ausgedrückt), IEnumerables als In-Memory-Sammlungen und IQueryables als Ausdrucksbäume zu betrachten. Der Punkt ist, dass die IQueryable-Schnittstelle die IEnumerable-Schnittstelle erbt, sodass die Ergebnisse dieser Abfrage aufgelistet werden können, wenn sie eine Abfrage darstellen. Durch die Aufzählung wird der einem IQueryable-Objekt zugeordnete Ausdrucksbaum ausgeführt. Tatsächlich können Sie also kein IEnumerable-Mitglied aufrufen, ohne das Objekt im Speicher zu haben. Es wird dort hineinkommen, wenn Sie es tun, wenn es nicht leer ist. IQueryables sind nur Abfragen, nicht die Daten.