Wie und wann werden LINQ-Abfragen übersetzt und ausgewertet?

Ich habe eine LINQ to SQL-Abfrage wie folgt:

var carIds = from car in _db.Cars where car.Color == 'Blue' select car.Id; 

Das obige wird schließlich in den folgenden sql übersetzt:

 select Id from Cars where Color = 'Blue' 

Ich habe gelesen, dass die Phasen sind:

  1. LINQ zu Lambda-Ausdruck
  2. Lambda-Ausdruck zu Ausdrucksbäumen
  3. Ausdrucksbäume zu SQL-statementen
  4. SQL-statementen werden ausgeführt

Also, meine Fragen sind wann und wie wird das übersetzt und ausgeführt?

Ich weiß, dass Phase 4 zur Laufzeit passiert, wenn auf meine Variable "carIds" innerhalb einer foreach-loop zugegriffen wird.

 foreach(var carId in carIds) // here? { Console.Writeline(carId) // or here? } 

Was ist mit den anderen Phasen? Wann treten sie auf? zur Kompilierzeit oder zur Laufzeit? bei welcher Zeile (bei der Definition oder nach der Definition, beim Zugriff oder vor dem Zugriff)?

Solutions Collecting From Web of "Wie und wann werden LINQ-Abfragen übersetzt und ausgewertet?"

Worüber Sie sprechen, ist die verzögerte Ausführung – im Wesentlichen wird die linq to SQL-Abfrage zu dem timepunkt ausgeführt, an dem Sie versuchen, die Ergebnisse aufzuzählen (z. B. iterieren Sie sie, .ToArray () usw.).

In Ihrem Beispiel wird die statement in der folgenden Zeile ausgeführt:

 foreach(var carId in carIds) 

Siehe diesen MSDN-Artikel zu LINQ und verzögerter Ausführung

  1. wird entweder von Ihnen, dem Entwickler oder dem C # -Compiler ausgeführt; Sie können den Lambda-Ausdruck manuell schreiben, oder mit LINQ-Syntax können sie von LINQ impliziert werden (dh where x.Foo == 12 behandelt wird, als wäre es .Where(x => x.Foo == 12) , und dann der Compiler verarbeitet Schritt 2 basierend darauf)
  2. erfolgt durch den C # -Compiler; es übersetzt den "Lambda-Ausdruck" in IL, der einen Ausdrucksbaum konstruiert (oder wiederverwendet, wo möglich). Man könnte auch argumentieren, dass die Laufzeit das ist, was diesen Schritt verarbeitet
  3. und
  4. werden typischerweise ausgeführt, wenn die Abfrage aufgelistet wird , insbesondere wenn foreach GetEnumerator() (und möglicherweise sogar auf den ersten .MoveNext )

die verzögerte Ausführung ermöglicht das Erstellen von Abfragen; beispielsweise:

 var query = ...something complex... var items = query.Take(20).ToList(); 

Hier wird der Take(20) als Teil der gesamten Abfrage ausgeführt, so dass Sie nicht alles vom server zurückholen und dann (beim Aufrufer) ihn auf die ersten 20 filtern; Nur 20 Zeilen werden jemals vom server abgerufen.

Sie können kompilierte Abfragen auch verwenden, um die Abfrage auf Schritt 4 zu beschränken (oder die Schritte 3 und 4 in einigen Fällen, in denen die Abfrage je nach Parameter einen anderen TSQL benötigt). Oder Sie können alles außer 4 überspringen, wenn Sie nur mit TSQL schreiben.

Sie sollten auch einen Schritt "5" hinzufügen: Materialisierung; Das ist ein ziemlich komplexer und wichtiger Schritt. Nach meiner Erfahrung kann der Materialisierungsschritt in Bezug auf die Gesamtleistung tatsächlich der bedeutendste sein (weshalb wir "dapper" geschrieben haben).