Ü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 ProjektionenTypumwandlungen 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: