Übersetzen von Standardabfrageoperatoren

LINQ to SQL übersetzt Standardabfrageoperatoren in SQL-Befehle. Der Abfrageprozessor der Datenbank bestimmt die Ausführungssemantik der SQL-Übersetzung.

Standardabfrageoperatoren werden anhand von Sequenzen definiert. Eine Sequenz wird sortiert und basiert auf der Verweisidentität für jedes Element der Sequenz. Weitere Informationen finden Sie unter Übersicht über Standardabfrageoperatoren (C#) oder Übersicht über Standardabfrageoperatoren (Visual Basic).

SQL verarbeitet hauptsächlich unsortierte Wertesätzen. Die Sortierung ist in der Regel ein explizit angegebener, nachgelagerter Prozess, der auf das Endergebnis einer Abfrage und nicht auf Zwischenergebnisse angewendet wird. Die Identität wird durch Werte definiert. Aus diesem Grund befassen sich SQL-Abfragen mit Multisets (so genannten bags) und nicht mit Sätzen.

Die folgenden Abschnitte beschreiben die Unterschiede zwischen den Standardabfrageoperatoren und ihren SQL-Übersetzungen für den SQL Server-Anbieter für LINQ to SQL.

Operatorunterstützung

Concat

Die Concat-Methode ist für geordnete Multisets definiert, bei denen die Reihenfolge des Empfängers und des Arguments identisch ist. Concat fungiert als UNION ALL für die Multisets, gefolgt von der allgemeinen Reihenfolge.

Die Sortierung in SQL ist der letzte Schritt vor dem Erzeugen von Ergebnissen. Concat behält die Reihenfolge der Argumente nicht bei. Um die entsprechende Sortierung sicherzustellen, müssen Sie die Ergebnisse von Concat explizit sortieren.

Intersect, Except, Union

Die Intersect-Methode und die Except-Methode sind nur für Sätze gut definiert. Die Semantik für Multisets ist nicht definiert.

Die Union-Methode ist für Multisets definiert, und zwar als unsortierte Verkettung der Multisets (effektiv als Ergebnis der UNION ALL-Klausel in SQL).

Take, Skip

Die Methoden Take und Skip sind nur für sortierte Sätze gut definiert. Die Semantik für ungeordnete Sätze oder Multisets ist nicht definiert.

Hinweis

Take und Skip weisen bestimmte Einschränkungen auf, wenn sie für Abfragen in SQL Server 2000 verwendet werden. Weitere Informationen finden Sie im Eintrag „Überspringen und Behandeln von Ausnahmen in SQL Server 2000“ unter Problembehandlung.

Aufgrund der Einschränkungen bei der Sortierung in SQL versucht LINQ to SQL, die Sortierung der Methodenargumente auf das Methodenergebnis zu verlagern. Betrachten Sie beispielsweise die folgende LINQ to SQL-Abfrage:

var custQuery =
    (from cust in db.Customers
    where cust.City == "London"
    orderby cust.CustomerID
    select cust).Skip(1).Take(1);
Dim custQuery = _
    From cust In db.Customers _
    Where cust.City = "London" _
    Order By cust.CustomerID _
    Select cust Skip 1 Take 1

Das generierte SQL für diesen Code verschiebt die Sortierung wie folgt ans Ende:

SELECT TOP 1 [t0].[CustomerID], [t0].[CompanyName],
FROM [Customers] AS [t0]
WHERE (NOT (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT TOP 1 [t1].[CustomerID]
        FROM [Customers] AS [t1]
        WHERE [t1].[City] = @p0
        ORDER BY [t1].[CustomerID]
        ) AS [t2]
    WHERE [t0].[CustomerID] = [t2].[CustomerID]
    ))) AND ([t0].[City] = @p1)
ORDER BY [t0].[CustomerID]

Es ist offensichtlich, dass die angegebene Sortierung konsistent sein muss, wenn Take und Skip verkettet werden. Andernfalls sind die Ergebnisse nicht definiert.

Take und Skip sind für nicht negative, konstante Integralargumente auf der Basis der Spezifikation für Standardabfrageoperatoren gut definiert.

Operatoren ohne Übersetzung

Die folgenden Methoden werden von LINQ to SQL nicht übersetzt. Der einfachste Grund ist der Unterschied zwischen ungeordneten Multisets und Sequenzen.

Operatoren Sinn
TakeWhile, SkipWhile SQL-Abfragen verwenden Multisets, keine Sequenzen. ORDER BY muss die letzte Klausel sein, die auf die Ergebnisse angewendet wird. Aus diesem Grund gibt es keine allgemeine Übersetzung dieser beiden Methoden.
Reverse Die Übersetzung dieser Methode ist für einen sortierten Satz möglich, erfolgt derzeit in LINQ to SQL aber nicht.
Last, LastOrDefault Die Übersetzung dieser Methoden ist für einen sortierten Satz möglich, erfolgt derzeit in LINQ to SQL aber nicht.
ElementAt, ElementAtOrDefault SQL-Abfragen verwenden Multisets, keine indizierbaren Sequenzen.
DefaultIfEmpty (Überladung mit Standard-arg) Im Allgemeinen kann ein Standardwert nicht für ein beliebiges Tupel angegeben werden. NULL-Werte für Tupel sind in einigen Fällen durch äußere Joins möglich.

Ausdrucksübersetzung

NULL-Semantik

LINQ to SQL erzwingt keine NULL-Vergleichssemantik in SQL. Vergleichsoperatoren werden syntaktisch zu ihren SQL-Entsprechungen übersetzt. Aus diesem Grund reflektiert die Semantik SQL-Semantik, die von Server- oder Verbindungseinstellungen definiert wird. So werden zwei NULL-Werte in den standardmäßigen SQL-Einstellungen beispielsweise als ungleich betrachtet, wobei Sie jedoch die Einstellungen ändern können, um die Semantik anzupassen. LINQ to SQL berücksichtigt beim Übersetzen von Abfragen keine Servereinstellungen.

Ein Vergleich mit dem NULL-Literal wird in die entsprechende SQL-Version (is null oder is not null) übersetzt.

Der Wert von null in der Zusammenstellung wird von SQL-Server definiert. LINQ to SQL ändert die Sortierung nicht.

Aggregate

Die Aggregationsmethode für Standardabfrageoperatoren Sum ergibt bei einer leeren Sequenz oder bei einer aus Nullen bestehenden Sequenz 0. In LINQ to SQL bleibt die Semantik von SQL unverändert, und Sum wird bei einer leeren oder nur aus Nullen bestehenden Sequenz zu null ausgewertet, nicht zu 0.

SQL-Einschränkungen für Zwischenergebnisse gelten in LINQ to SQL für Aggregate. Die Sum von 32-Bit-Ganzzahlmengen wird nicht berechnet, indem man 64-Bit-Ergebnisse verwendet. Bei einer LINQ to SQL-Übersetzung von Sum kann es zu einem Überlauf kommen, auch wenn die Implementierung der Standardabfrageoperatoren bei der entsprechenden Sequenz im Arbeitsspeicher keinen Überlauf verursacht.

Ebenso wird die LINQ to SQL-Übersetzung von Average von Ganzzahlenwerten als integer berechnet, nicht als double.

Entitätsargumente

LINQ to SQL ermöglicht die Verwendung von Entitätstypen in den Methoden GroupBy und OrderBy. In der Übersetzung dieser Operatoren gilt die Verwendung eines Arguments als Entsprechung zur Angabe aller Member dieses Typs. Der folgende Code ist z. B. äquivalent:

db.Customers.GroupBy(c => c);
db.Customers.GroupBy(c => new { c.CustomerID, c.ContactName });
db.Customers.GroupBy(Function(c) c)
db.Customers.GroupBy(Function(c) New With {c.CustomerID, _
    c.ContactName})

Gleichwertige/vergleichbare Argumente

Die Gleichheit von Argumenten ist in der Implementierung der folgenden Methoden erforderlich:

LINQ to SQL unterstützt Gleichheit und Vergleich für flache Argumente, aber nicht für Argumente, die Sequenzen sind oder enthalten. Ein flaches Argument ist ein Typ, der einer SQL-Zeile zugeordnet werden kann. Eine Projektion von einem oder mehreren statisch festgelegten Entitätstypen ohne Sequenz gilt als flaches Argument.

Hier sehen Sie einige Beispiele für flache Argumente:

db.Customers.Select(c => c);
db.Customers.Select(c => new { c.CustomerID, c.City });
db.Orders.Select(o => new { o.OrderID, o.Customer.City });
db.Orders.Select(o => new { o.OrderID, o.Customer });	
db.Customers.Select(Function(c) c)
db.Customers.Select(Function(c) New With {c.CustomerID, c.City})
db.Orders.Select(Function(o) New With {o.OrderID, o.Customer.City})
db.Orders.Select(Function(o) New With {o.OrderID, o.Customer})

Hier sehen Sie einige Beispiele für nicht flache (hierarchische) Argumente:

// In the following line, c.Orders is a sequence.
db.Customers.Select(c => new { c.CustomerID, c.Orders });
// In the following line, the result has a sequence.
db.Customers.GroupBy(c => c.City);
' In the following line, c.Orders is a sequence.
db.Customers.Select(Function(c) New With {c.CustomerID, c.Orders})
' In the following line, the result has a sequence.
db.Customers.GroupBy(Function(c) c.City)

Visual Basic-Funktionsübersetzung

Die folgenden, vom Visual Basic-Compiler verwendeten Hilfsfunktionen werden in entsprechende SQL-Operatoren und -Funktionen übersetzt:

  • CompareString

  • DateTime.Compare

  • Decimal.Compare

  • IIf (in Microsoft.VisualBasic.Interaction)

Konvertierungsmethoden:

  • ToBoolean
  • ToSByte
  • ToByte
  • ToChar
  • ToCharArrayRankOne
  • ToDate
  • ToDecimal
  • ToDouble
  • ToInteger
  • ToUInteger
  • ToLong
  • ToULong
  • ToShort
  • ToUShort
  • ToSingle
  • ToString

Unterstützung von Vererbung

Einschränkungen der Vererbungszuordnung

Weitere Informationen finden Sie unter Gewusst wie: Zuordnen von Vererbungshierarchien.

Vererbung in Abfragen

C#-Umwandlungen werden nur in Projektionen unterstützt. An anderer Stelle verwendete Umwandlungen werden nicht übersetzt, sonder ignoriert. Neben den SQL-Funktionsnamen führt SQL tatsächlich nur das Äquivalent von Common Language Runtime (CLR) Convert aus. Das heißt, SQL kann den Wert eines Typs ändern. Es gibt keine Entsprechung zu CLR-Umwandlungen, da es kein Konzept für die Neuinterpretation der gleichen Bits als die eines anderen Typs gibt. Aus diesem Grund funktioniert eine C#-Umwandlung nur lokal. Eine Remoteausführung ist nicht möglich.

Der is-Operator und der as-Operator sowie die GetType-Methode werden nicht auf den Select-Operator beschränkt. Sie können auch in anderen Abfrageoperatoren verwendet werden.

SQL Server 2008-Unterstützung

Ab .NET Framework 3.5 SP1 unterstützt LINQ to SQL das Mapping zu den in SQL Server 2008 neu eingeführten Datums- und Uhrzeittypen. Einschränkungen bestehen jedoch für die LINQ to SQL-Abfrageoperatoren, die beim Arbeiten mit den diesen neuen Typen zugeordneten Werten verwendet werden können.

Nicht unterstützte Abfrageoperatoren

Die folgenden Abfrageoperatoren werden nicht für Werte unterstützt, die den neuen Datums- und Uhrzeittypen von SQL Server zugeordnet sind: DATETIME2, DATE, TIME und DATETIMEOFFSET.

  • Aggregate

  • Average

  • LastOrDefault

  • OfType

  • Sum

Weitere Informationen zum Zuordnen zu diesen Datums- und Uhrzeittypen von SQL Server finden Sie unter SQL-CLR-Typzuordnung.

SQL Server 2005-Unterstützung

LINQ to SQL bietet keine Unterstützung für die folgenden SQL Server 2005-Funktionen:

  • Gespeicherte Prozeduren für SQL CLR.

  • Benutzerdefinierter Typ.

  • XML-Abfragefunktionen.

SQL Server 2000-Unterstützung

Die folgenden SQL Server 2000-Einschränkungen (im Vergleich zu Microsoft SQL Server 2005) wirken sich auf LINQ to SQL-Unterstützung aus.

Cross Apply-Operator und Outer Apply-Operator

Diese Operatoren sind in SQL Server 2000 nicht verfügbar. LINQ to SQL versucht eine Reihe von erneuten Schreibvorgängen, um sie durch entsprechende Joins zu ersetzen.

Cross Apply und Outer Apply werden für Beziehungsnavigation erzeugt. Der Satz von Abfragen, für den solche erneuten Schreibzugriffe möglich sind, ist nicht klar definiert. Aus diesem Grund ist der minimale für SQL Server 2000 unterstützte Abfragesatz derjenige Satz, der keine Beziehungsnavigation beinhaltet.

text / ntext

Die Datentypen text / ntext können in bestimmten Abfragevorgängen für varchar(max) / nvarchar(max), die von Microsoft SQL Server 2005 unterstützt werden, nicht verwendet werden.

Für diese Einschränkung ist keine Lösung verfügbar. Sie können insbesondere Distinct() nicht für Ergebnisse verwenden, die Member enthalten, die der text-Spalte oder der ntext-Spalte zugeordnet sind.

Von verschachtelten Abfragen ausgelöstes Verhalten

Die Bindung von SQL Server 2000 (bis einschließlich SP4) weist einige Eigenheiten auf, die durch geschachtelte Abfragen ausgelöst werden. Der Satz von SQL-Abfragen, der diese Eigenheiten auslöst, ist nicht klar definiert. Aus diesem Grund können Sie den Satz von LINQ to SQL-Abfragen, der zu SQL Server-Ausnahmen führen kann, nicht definieren.

Skip-Operator und Take-Operator

Take und Skip weisen bestimmte Einschränkungen auf, wenn sie für Abfragen in SQL Server 2000 verwendet werden. Weitere Informationen finden Sie im Eintrag „Überspringen und Behandeln von Ausnahmen in SQL Server 2000“ unter Problembehandlung.

Objektmaterialisierung

Die Materialisierung erstellt CLR-Objekte aus Zeilen, die von einer oder mehreren SQL-Abfragen zurückgegeben werden.

  • Die folgenden Aufrufe werden im Rahmen der Materialisierung lokal ausgeführt:

    • Konstruktoren

    • ToString-Methoden in Projektionen

    • Typumwandlungen in Projektionen

  • Methoden, die der AsEnumerable-Methode folgen, werden lokal ausgeführt. Diese Methode verursacht keine unmittelbare Ausführung.

  • Sie können einen struct als Rückgabetyp eines Abfrageergebnisses oder als Member des Ergebnistyps verwenden. Entitäten müssen Klassen sein. Anonyme Typen werden als Klasseninstanzen materialisiert. Benannte structs (Nicht-Entitäten) können jedoch in Projektionen verwendet werden.

  • Ein Member des Rückgabetyps eines Abfrageergebnisses kann vom Typ IQueryable<T> sein. Er wird als lokale Auflistung materialisiert.

  • Die folgenden Methoden lösen eine unmittelbare Materialisierung der Sequenz aus, auf die die Methoden angewendet werden:

Weitere Informationen