wake-up-neo.com

Unterschiede zwischen IQueryable, List, IEnumerator?

Ich frage mich, was der Unterschied zwischen IQueryable, List, IEnumerator ist und wann ich welche verwenden soll.

Wenn ich beispielsweise Linq to SQL verwende, würde ich Folgendes tun:

public List<User> GetUsers()
{
   return db.User.where(/* some query here */).ToList();
}

Jetzt frage ich mich, ob ich stattdessen IQueryable verwenden soll. Ich bin mir nicht sicher, welche Vorteile die Verwendung gegenüber der Liste hat.

61
chobo2

Mit IQueryable<T> Kann ein Abfrageanbieter (z. B. ein ORM wie LINQ to SQL oder das Entity Framework) die in einer Abfrage enthaltenen Ausdrücke verwenden, um die Anforderung in ein anderes Format zu übersetzen. Mit anderen Worten, LINQ-to-SQL untersucht die Eigenschaften der Entitäten, die Sie verwenden, zusammen mit den Vergleichen, die Sie durchführen, und erstellt tatsächlich eine SQL-Anweisung, um (hoffentlich) eine äquivalente Anforderung auszudrücken.

IEnumerable<T> Ist allgemeiner als IQueryable<T> (Obwohl alle Instanzen von IQueryable<T>IEnumerable<T> Implementieren) und definiert nur eine Sequenz. In der Klasse Enumerable stehen jedoch Erweiterungsmethoden zur Verfügung, mit denen einige Abfragetypoperatoren für diese Schnittstelle definiert werden und mit normalem Code diese Bedingungen ausgewertet werden.

List<T> Ist nur ein Ausgabeformat und steht, obwohl es IEnumerable<T> Implementiert, nicht in direktem Zusammenhang mit der Abfrage.

Mit anderen Worten, wenn Sie IQueryable<T> Verwenden, definieren Sie einen Ausdruck, der in etwas anderes übersetzt wird. Obwohl Sie Code schreiben, wird dieser Code nie ausgeführt, sondern nur überprüft ​​und in etwas anderes umgewandelt, wie eine tatsächliche SQL-Abfrage. Aus diesem Grund sind in diesen Ausdrücken nur bestimmte Dinge gültig. Beispielsweise können Sie keine gewöhnliche Funktion aufrufen, die Sie in diesen Ausdrücken definieren, da LINQ-to-SQL nicht weiß, wie Sie Ihren Aufruf in eine SQL-Anweisung umwandeln können. Die meisten dieser Einschränkungen werden leider nur zur Laufzeit ausgewertet.

Wenn Sie zum Abfragen IEnumerable<T> Verwenden, verwenden Sie LINQ-to-Objects. Dies bedeutet, dass Sie den tatsächlichen Code schreiben, der zum Auswerten Ihrer Abfrage oder zum Transformieren der Ergebnisse verwendet wird Einschränkungen für das, was Sie tun können. Sie können andere Funktionen innerhalb dieser Ausdrücke frei aufrufen.

Mit LINQ to SQL

Hand in Hand mit der obigen Unterscheidung ist es auch wichtig zu bedenken, wie dies in der Praxis funktioniert. Wenn Sie in LINQ to SQL eine Abfrage für eine Datenkontextklasse schreiben, wird ein IQueryable<T> Erstellt. Was auch immer Sie tun gegen den IQueryable<T> Selbst ​​wird in SQL umgewandelt, sodass Ihre Filterung und Transformation auf dem Server durchgeführt wird. Was auch immer Sie dagegen tun als IEnumerable<T>, wird auf Anwendungsebene durchgeführt. Manchmal ist dies wünschenswert (wenn Sie beispielsweise clientseitigen Code verwenden müssen), aber in vielen Fällen ist dies nicht beabsichtigt.

Wenn ich beispielsweise einen Kontext mit einer Customers -Eigenschaft hatte, die eine Customer -Tabelle darstellt, und jeder Kunde eine CustomerId -Spalte hat, sehen wir uns zwei Möglichkeiten für diese Abfrage an:

var query = (from c in db.Customers where c.CustomerId == 5 select c).First();

Dadurch wird SQL generiert, das die Datenbank nach dem Customer -Datensatz mit einem CustomerId von 5 abfragt.

select CustomerId, FirstName, LastName from Customer where CustomerId = 5

Was passiert nun, wenn wir Customers mit der Erweiterungsmethode AsEnumerable() in einen IEnumerable<Customer> Umwandeln?

var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();

Diese einfache Änderung hat schwerwiegende Konsequenzen. Da wir Customers in einen IEnumerable<Customer> Umwandeln, wird die gesamte Tabelle zurückgebracht und auf der Clientseite gefiltert (genau genommen bringt dies jede Zeile in der Tabelle zurück - bis es auf eine trifft, die den Kriterien entspricht, aber der Punkt ist der gleiche).

ToList ()

Bisher haben wir nur über IQueryable und IEnumerable gesprochen. Dies liegt daran, dass es sich um ähnliche, komplementäre Schnittstellen handelt. In beiden Fällen definieren Sie query; Das heißt, Sie definieren wo, um die Daten zu finden, was Filter, die angewendet werden sollen, und was Daten, die zurückgegeben werden sollen. Beides sind Abfragen

query = from c in db.Customers where c.CustomerId == 5 select c;
query = from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c;

Wie bereits erwähnt, verwendet die erste Abfrage IQueryable und die zweite IEnumerable. In beiden Fällen ist dies jedoch nur eine Abfrage. Das Definieren der Abfrage hat keine Auswirkungen auf die Datenquelle. Die Abfrage wird tatsächlich ausgeführt, wenn der Code beginnt, die Liste zu durchlaufen. Dies kann auf verschiedene Arten geschehen. eine foreach -Schleife, die ToList() aufruft usw.

Die Abfrage wird ausgeführt das erste und jedes Mal, wenn sie iteriert wird. Wenn Sie ToList() zweimal auf query aufrufen, erhalten Sie zwei Listen mit völlig unterschiedlichen Objekten. Sie können dieselben Daten enthalten, aber es handelt sich um unterschiedliche Referenzen.

Nach Kommentaren bearbeiten

Ich möchte nur klarstellen, wann die Dinge clientseitig erledigt werden und wann sie serverseitig erledigt werden. Wenn Sie einen IQueryable<T> Als IEnumerable<T> Bezeichnen, nur erfolgt die Abfrage nach handelt es sich um einen IEnumerable<T> clientseitig erledigt. Angenommen, ich habe diese Tabelle und einen LINQ-to-SQL-Kontext:

Customer
-----------
CustomerId
FirstName
LastName

Ich erstelle eine Abfrage basierend auf FirstName. Dies erzeugt einen IQueryable<Customer>:

var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c;

Jetzt übergebe ich diese Abfrage an eine Funktion, die einen IEnumerable<Customer> Und eine auf LastName basierende Filterung vornimmt:

public void DoStuff(IEnumerable<Customer> customers)
{
    foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
    {
        Console.WriteLine(cust.CustomerId);
    }
}

Wir haben hier eine zweite Abfrage durchgeführt, die jedoch mit einem IEnumerable<Customer> Durchgeführt wird. Was hier passieren wird, ist, dass die erste Abfrage ausgewertet wird und diese SQL ausführt:

select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%'

Also bringen wir alle zurück, die FirstName sind und mit "Ad" Beginnen. Beachten Sie, dass hier nichts über LastName steht. Das liegt daran, dass es clientseitig herausgefiltert wird.

Sobald es diese Ergebnisse zurückbringt, durchläuft das Programm die Ergebnisse und liefert nur die Datensätze, deren LastName mit "Ro" Beginnt. Der Nachteil dabei ist, dass wir Daten zurückgebracht haben - nämlich alle Zeilen, deren LastNamenicht ​​mit "Ro" Beginnt - dass könnte wurden auf dem Server ausgefiltert.

107
Adam Robinson

IQueryable<T>: Abstrahiert den Datenbankzugriff und unterstützt die verzögerte Auswertung von Abfragen
List<T>: Eine Sammlung von Einträgen. Keine Unterstützung von Lazy Evaluation
IEnumerator<T>: Liefert die Fähigkeit des Iterierens über und IEnumerable<T> (Welche sowohl IQueryable<T> Als auch List<T> Sind)

Das Problem mit diesem Code ist recht einfach - er führt die Abfrage immer aus, wenn er aufgerufen wird. Wenn Sie stattdessen db.User.Where(...) zurückgeben würden (dies ist ein IQueryable<T>), Würden Sie die Auswertung der Abfrage zurückhalten, bis sie tatsächlich benötigt wird (iteriert). Wenn der Benutzer dieser Methode weitere Vergleichselemente angeben müsste, werden diese auch in der Datenbank ausgeführt, was die Ausführung erheblich beschleunigt.

8
Femaref

Verwenden Sie iList oder List<item>, wenn Sie eine stark typisierte Auflistung einer Entität möchten.

Und verwenden Sie Iqueryable und Ienumurator, wenn Sie dumme Daten als Sammlung von Objekten erhalten möchten. Sie werden als lose Typensammlung zurückgegeben, und es gelten keine Einschränkungen.

Ich würde lieber List<type> weil mit einem Listenumbruch und in einer stark typisierten Auflistung meine Ergebnismenge umgewandelt.

Wenn Sie eine Liste verwenden, können Sie auch Layer in Array, Ienumurator oder als abfragbar hinzufügen, sortieren und konvertieren.

0
Mahmoud Sayed