Freigeben über


Abfrageausführung

Nachdem eine LINQ-Abfrage von einem Benutzer erstellt wurde, wird sie in eine Befehlsstruktur konvertiert. Eine Befehlsstruktur ist eine Darstellung einer Abfrage, die mit dem Entity Framework kompatibel ist. Die Befehlsstruktur wird dann für die Datenquelle ausgeführt. Während der Ausführung der Abfrage werden alle Abfrageausdrücke (d. h. alle Komponenten der Abfrage) ausgewertet, einschließlich der Ausdrücke, die für die Materialisierung der Ergebnisse verwendet werden.

An welchem Punkt Abfrageausdrücke ausgeführt werden ist unterschiedlich. LINQ-Abfragen werden nicht bei der Erstellung der Abfragevariablen, sondern beim Durchlaufen der Abfragevariablen ausgeführt. Dies wird als verzögerte Ausführung bezeichnet. Eine sofortige Ausführung der Abfrage kann auch erzwungen werden. Dies ist für die Zwischenspeicherung von Abfrageergebnissen sinnvoll. Dies wird weiter unten in diesem Thema beschrieben.

Beim Ausführen einer LINQ to Entities-Abfrage werden möglicherweise einige Ausdrücke auf dem Server und andere lokal auf dem Client ausgeführt. Ein Ausdruck wird auf dem Client ausgewertet, bevor die Abfrage auf dem Server ausgeführt wird. Wenn ein Ausdruck auf dem Client ausgewertet wird, wird dieser Ausdruck in der Abfrage durch das Ergebnis der Auswertung ersetzt. Anschließend wird die Abfrage auf dem Server ausgeführt. Da Abfragen für die Datenquelle ausgeführt werden, wird das auf dem Client festgelegte Verhalten von der Konfiguration der Datenquelle überschrieben. Zum Beispiel hängen die Behandlung von NULL-Werten und die numerische Genauigkeit von den Servereinstellungen ab. Alle Ausnahmen, die bei der Abfrageausführung auf dem Server ausgelöst wurden, werden direkt an den Client weitergegeben.

Verzögerte Ausführung

Die Abfragevariable speichert die Abfragebefehle nur dann, wenn die Abfrage so entwickelt wurde, dass sie eine Sequenz von Werten zurückgibt. Wenn die Abfrage keine Methode enthält, die eine sofortige Ausführung erzwingt, wird die Ausführung der Abfrage so lange verzögert, bis die Abfragevariable in einer foreach-Schleife oder einer For Each-Schleife durchlaufen wird. Die Abfrage wird bei jedem Durchlaufen der Abfragevariablen in einer foreach-Schleife oder einer For Each-Schleife auf dem Server ausgeführt. Dies wird als verzögerte Ausführung bezeichnet. Die verzögerte Ausführung ermöglicht die Kombination mehrerer Abfragen oder die Erweiterung einer Abfrage. Dabei werden der Abfrage neue Operationen hinzugefügt. Die Änderungen werden dann bei der Ausführung der Abfrage berücksichtigt. Die Abfragevariable selbst enthält keine Abfrageergebnisse. Daher kann eine Abfrage beliebig oft ausgeführt werden. Sie könnten beispielsweise über eine Datenbank verfügen, die ständig durch eine separate Anwendung aktualisiert wird. Sie könnten in Ihrer Anwendung eine Abfrage erstellen, die die neuesten Daten abruft, und Sie könnten diese Abfrage in bestimmten Abständen wiederholt ausführen, um bei jeder Ausführung andere Ergebnisse abzurufen.

LINQ to Entities-Abfragen werden im Entity Framework in Befehlsstrukturen konvertiert und beim Durchlaufen der Ergebnisse in der Datenquelle ausgeführt. Konvertierungsfehler führen an diesem Punkt zu Ausnahmen, die im Client ausgelöst werden.

Sofortige Ausführung

Im Gegensatz zur verzögerten Ausführung von Abfragen, die eine Sequenz von Werten zurückgeben, werden Abfragen, die einen Singleton-Wert zurückgeben, sofort ausgeführt. Einige Beispiele für SINGLETON-Abfragen sind Average, Count, First und Max. Diese werden sofort ausgeführt, da die Abfrage eine Sequenz für die Berechnung des Singleton-Ergebnisses erzeugen muss. Eine unmittelbare Ausführung kann auch erzwungen werden. Dies ist nützlich, wenn die Ergebnisse einer Abfrage zwischengespeichert werden sollen. Zur Erzwingung der sofortigen Ausführung einer Abfrage, die keinen Singleton-Wert zurückgibt, kann die ToList-Methode, die ToDictionary-Methode oder die ToArray-Methode für eine Abfrage oder eine Abfragevariable aufgerufen werden. Im folgenden Beispiel wird die ToArray-Methode verwendet, um eine Sequenz unmittelbar in ein Array auszuwerten.

Using AWEntities As New AdventureWorksEntities
    Dim products As ObjectQuery(Of Product) = AWEntities.Product

    Dim prodArray As Product() = ( _
        From product In products _
        Order By product.ListPrice Descending _
        Select product).ToArray()

    Console.WriteLine("The list price from highest to lowest:")
    For Each prod As Product In prodArray
        Console.WriteLine(prod.ListPrice)
    Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    ObjectQuery<Product> products = AWEntities.Product;

    Product[] prodArray = (
        from product in products
        orderby product.ListPrice descending
        select product).ToArray();

    Console.WriteLine("Every price from highest to lowest:");
    foreach (Product product in prodArray)
    {
        Console.WriteLine(product.ListPrice);
    }
}

Die Ausführung kann auch erzwungen werden, indem die foreach-Schleife oder die For Each-Schleife unmittelbar hinter den Abfrageausdruck gestellt wird. Mit einem Aufruf von ToList oder ToArray werden jedoch alle Daten in einem einzelnen Auflistungsobjekt zwischengespeichert.

Speicherausführung

Im Allgemeinen werden Ausdrücke in LINQ to Entities auf dem Server ausgewertet, und das Verhalten der Ausdrücke richtet sich nicht nach der CLR-Semantik, sondern nach der Semantik der Datenquelle. Dies gilt jedoch nicht, wenn der Ausdruck z. B. auf dem Client ausgeführt wird. Dies kann zu unerwarteten Ergebnissen führen, wenn sich beispielsweise Server und Client in unterschiedlichen Zeitzonen befinden.

Einige Ausdrücke in der Abfrage werden möglicherweise auf dem Client ausgeführt. Im Allgemeinen findet der größte Teil der Abfrageausführung auf dem Server statt. Abgesehen von Methoden, die für der Datenquelle zugeordnete Abfrageelemente ausgeführt werden, treten in der Abfrage häufig Ausdrücke auf, die lokal ausgeführt werden können. Bei der lokalen Ausführung eines Abfrageausdrucks wird ein Wert zurückgegeben, der für die Ausführung der Abfrage oder die Konstruktion des Ergebnisses verwendet werden kann.

Bestimmte Operationen werden stets auf dem Client ausgeführt. Dazu gehören die Bindung von Werten, Unterausdrücken und Unterabfragen von Abschlüssen sowie die Materialisierung von Objekten in Abfrageergebnisse. Das bedeutet, dass diese Elemente (beispielsweise Parameterwerte) während der Ausführung nicht aktualisiert werden können. Anonyme Typen können inline in der Datenquelle erstellt werden. Davon sollte jedoch nicht ausgegangen werden. Inlinegruppierungen können ebenfalls in der Datenquelle erstellt werden. Davon sollte jedoch nicht in jedem Fall ausgegangen werden. Im Allgemeinen sollten keine Annahmen darüber gemacht werden, was auf dem Server erstellt wird.

In diesem Abschnitt werden die Szenarios, in denen Code lokal auf dem Client ausgeführt wird, beschrieben. Weitere Informationen darüber, welche Typen von Ausdrücken lokal ausgeführt werden, finden Sie unter Ausdrücke in LINQ to Entities-Abfragen.

Literale und Parameter

Lokale Variablen, wie die orderID-Variable im folgenden Beispiel, werden auf dem Client ausgewertet.

Dim sales As ObjectQuery(Of SalesOrderHeader) = AWEntities.SalesOrderHeader

Dim orderID As Integer = 51987

Dim salesInfo = _
    From s In sales _
    Where s.SalesOrderID = orderID _
    Select s
ObjectQuery<SalesOrderHeader> sales = AWEntities.SalesOrderHeader;

int orderID = 51987;

IQueryable<SalesOrderHeader> salesInfo =
    from s in sales
    where s.SalesOrderID == orderID
    select s;

Methodenparameter werden ebenfalls auf dem Client ausgewertet. Der unten der MethodParameterExample-Methode übergebene orderID-Parameter ist ein Beispiel dafür.

Function MethodParameterExample(ByVal orderID As Integer)
    Using AWEntities As New AdventureWorksEntities()

        Dim sales As ObjectQuery(Of SalesOrderHeader) = AWEntities.SalesOrderHeader

        Dim salesInfo = _
            From s In sales _
            Where s.SalesOrderID = orderID _
            Select s

        Console.WriteLine("Sales order info:")
        For Each sale As SalesOrderHeader In salesInfo
            Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue)
        Next
    End Using

End Function
public static void MethodParameterExample(int orderID)
{
    using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
    {
        
        ObjectQuery<SalesOrderHeader> sales = AWEntities.SalesOrderHeader;
                        
        IQueryable<SalesOrderHeader> salesInfo =
            from s in sales
            where s.SalesOrderID == orderID
            select s;                

        foreach (SalesOrderHeader sale in salesInfo)
        {
            Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue);
        }
    }
}

Umwandeln von Literalen auf dem Client

Umwandlungen von null in einen CLR-Typ werden auf dem Client ausgeführt:

Dim contacts As ObjectQuery(Of Contact) = AWEntities.Contact

Dim query = _
    From c In contacts _
    Where c.EmailAddress = CType(Nothing, String) _
    Select c
ObjectQuery<Contact> contacts = AWEntities.Contact;

IQueryable<Contact> query =
    from c in contacts
    where c.EmailAddress == (string)null
    select c;

Umwandlungen in einen Typ, wie ein Decimal, das NULL-Werte zulässt, werden auf dem Client ausgeführt:

Dim products As ObjectQuery(Of Product) = AWEntities.Product

Dim query = _
    From product In products _
        Where product.Weight = CType(23.77, Decimal?) _
        Select product
ObjectQuery<Product> products = AWEntities.Product;

IQueryable<Product> query =
    from product in products
    where product.Weight == (decimal?)23.77
    select product;

Konstruktoren für Literale

Neue CLR-Typen, die EDM-Typen zugeordnet werden können, werden auf dem Client ausgeführt:

Dim products As ObjectQuery(Of Product) = AWEntities.Product

Dim query = _
    From product In products _
    Where product.Weight = New Decimal(23.77) _
    Select product
ObjectQuery<Product> products = AWEntities.Product;

IQueryable<Product> query =
    from product in products
    where product.Weight == new decimal(23.77)
    select product;

Neue Arrays werden ebenfalls auf dem Client ausgeführt.

Speicherausnahmen

Während der Abfrageausführung auftretende Speicherfehler werden an den Client übergeben und nicht zugeordnet oder behandelt.

Speicherkonfiguration

Bei der Abfrageausführung im Speicher überschreibt die Speicherkonfiguration das Clientverhalten, und für alle Operationen und Ausdrücke wird Speichersemantik verwendet. Das kann zu unterschiedlichem Verhalten von CLR- und Speicherausführung in Bereichen wie NULL-Vergleichen, GUID-Sortierung, Genauigkeit von Operationen mit nicht präzisen Datentypen (wie Gleitkommatypen oder DateTime) sowie Zeichenfolgenoperationen führen. Bei der Beurteilung von Abfrageergebnissen sollten diese Punkte beachtet werden.

Folgende Beispiele zeigen einige Unterschiede im Verhalten zwischen der CLR und SQL Server:

  • SQL Server sortiert GUIDs anders als die CLR.

  • Es können auch Unterschiede in der Ergebnisgenauigkeit beim Arbeiten mit dem Decimal-Typ auf SQL Server auftreten. Der Grund dafür sind die Anforderungen fester Präzision des Dezimaltyps von SQL Server. Beispielsweise ist der Durchschnitt der Decimal-Werte 0,0 und 0,0 und 1,0 im Arbeitsspeicher des Clients 0,3333333333333333333333333333 und im Speicher 0,333333 (für die Standardpräzision des Dezimaltyps von SQL Server).

  • Einige Operationen für Zeichenfolgenvergleiche werden in SQL Server ebenfalls anders behandelt als in der CLR. Das Verhalten bei Zeichenfolgenvergleichen hängt von den Sortiereinstellungen auf dem Server ab.

  • Funktions- oder Methodenaufrufe, die in einer LINQ to Entities-Abfrage enthalten sind, werden kanonischen Funktionen im Entity Framework zugeordnet, die wiederum in Transact-SQL übersetzt und in der SQL Server-Datenbank ausgeführt werden. Es gibt Fälle, in denen sich das Verhalten dieser zugeordneten Funktionen von dem der Implementierung in den Basisklassenbibliotheken unterscheidet. Ein Aufruf der Contains, StartsWith-Methode und der EndsWith-Methode mit einer leeren Zeichenfolge als Parameter gibt bei der Ausführung in der CLR true zurück und bei der Ausführung in SQL Server false. Die EndsWith-Methode kann ebenfalls unterschiedliche Ergebnisse zurückgeben, da zwei Zeichenfolgen, die sich nur in nachfolgenden Leerzeichen unterscheiden, in SQL Server als gleich aufgefasst werden, in der CLR jedoch nicht. Dies wird im folgenden Beispiel illustriert:

Using AWEntities As New AdventureWorksEntities()

    Dim products As ObjectQuery(Of Product) = AWEntities.Product

    Dim query = _
        From p In products _
        Where p.Name = "Reflector" _
        Select p.Name

    Dim q = _
        query.Select(Function(c) c.EndsWith("Reflector "))

    Console.WriteLine("LINQ to Entities returns: " & q.First())
    Console.WriteLine("CLR returns: " & "Reflector".EndsWith("Reflector "))
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{

    ObjectQuery<Product> products = AWEntities.Product;

    IQueryable<string> query = from p in products
                               where p.Name == "Reflector"
                               select p.Name;

    IEnumerable<bool> q = query.Select(c => c.EndsWith("Reflector "));

    Console.WriteLine("LINQ to Entities returns: " + q.First());
    Console.WriteLine("CLR returns: " + "Reflector".EndsWith("Reflector "));

}

Siehe auch

Weitere Ressourcen

Abfragen mit LINQ to Entities