Traduction des opérateurs de requête standard

LINQ to SQL traduit les opérateurs de requête standard en commandes SQL. Le processeur de requêtes de la base de données détermine la sémantique d'exécution de la traduction SQL.

Les opérateurs de requête standard sont définis par rapport à des séquences. Une séquence est ordonnée et s’appuie sur l’identité de référence pour chaque élément de la séquence. Pour plus d’informations, consultez Vue d’ensemble des opérateurs de requête standard (C#) ou Vue d’ensemble des opérateurs de requête standard (Visual Basic).

SQL traite essentiellement des jeux de valeurs non ordonnés. Le classement est généralement une opération de post-traitement déclarée explicitement appliquée au résultat final d'une requête plutôt qu'à des résultats intermédiaires. L'identité est définie par des valeurs. Pour cette raison, les requêtes SQL sont censées traiter les multijeux (conteneurs) plutôt que les jeux.

Les paragraphes suivants décrivent les différences entre les opérateurs de requête standard et leur traduction SQL pour le fournisseur SQL Server de LINQ to SQL.

Prise en charge des opérateurs

Concat

La méthode Concat est définie pour des multijeux ordonnés lorsque les ordres du récepteur et de l’argument sont identiques. Concat fonctionne comme UNION ALL sur les multijeux suivis de l'ordre courant.

L'étape finale est le classement dans SQL avant que les résultats ne soient générés. Concat ne conserve pas l'ordre de ses arguments. Pour garantir un classement approprié, vous devez classer explicitement les résultats de Concat.

Intersect, Except, Union

Les méthodes Intersect et Except sont bien définies sur les jeux uniquement. La sémantique pour les multijeux n'est pas définie.

La méthode Union est définie pour les multijeux comme la concaténation non ordonnée des multijeux (le résultat de la clause UNION ALL dans SQL).

Take, Skip

Les méthodes Take et Skip sont bien définies sur les jeux ordonnés uniquement. La sémantique des jeux non ordonnés ou des multijeux n'est pas définie.

Notes

Take et Skip sont soumis à certaines limites lorsqu'ils sont utilisés dans des requêtes SQL Server 2000. Pour plus d’informations, consultez « Exceptions d’ignorance (Skip) et d’acceptation (Take) dans SQL Server 2000 » dans Résolution des problèmes.

Du fait des restrictions des classements dans SQL, LINQ to SQL tente de déplacer le classement de l’argument de ces méthodes vers le résultat de la méthode. Examinons, par exemple, la requête LINQ to SQL suivante :

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

Le SQL généré pour ce code déplace le classement à la fin, comme suit :

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]

Il devient évident que le classement spécifié doit être cohérent lorsque Take et Skip sont enchaînés. Si ce n'est pas le cas, les résultats ne sont pas définis.

Take et Skip sont bien définis pour des arguments intégraux constants non négatifs basés sur la spécification de l’opérateur de requête standard.

Opérateurs sans traduction

LINQ to SQL ne traduit pas les méthodes suivantes. La raison la plus courante est la différence entre les multijeux non ordonnés et les séquences.

Opérateurs Rationale
TakeWhile, SkipWhile Les requêtes SQL fonctionnent sur des multijeux et non sur des séquences. ORDER BY doit être la dernière clause appliquée aux résultats. Il n'existe donc aucune traduction à caractère général pour ces deux méthodes.
Reverse La traduction de cette méthode est possible pour un jeu ordonné mais n’est pas fournie par LINQ to SQL.
Last, LastOrDefault La traduction de ces méthodes est possible pour un jeu ordonné mais n’est pas fournie par LINQ to SQL.
ElementAt, ElementAtOrDefault Les requêtes SQL fonctionnent sur des multijeux et non sur des séquences indexables.
DefaultIfEmpty (surcharge avec argument par défaut) En général, il est impossible de spécifier une valeur par défaut pour un tuple arbitraire. Les valeurs null pour les tuples sont possibles dans certains cas par le biais de jointures externes.

Traduction d'expressions

Sémantique Null

LINQ to SQL n’impose pas de sémantique de comparaison null sur SQL. Les opérateurs de comparaison sont traduits syntaxiquement dans leurs équivalents SQL. Pour cette raison, la sémantique reflète la sémantique SQL définie par le serveur ou les paramètres de connexion. Par exemple, deux valeurs Null sont considérées comme différentes selon les paramètres SQL Server, mais vous pouvez modifier les paramètres pour changer la sémantique. LINQ to SQL ne tient pas compte des paramètres du serveur quand il traduit des requêtes.

Une comparaison avec le littéral null est traduite dans la version SQL appropriée (is null ou is not null).

La valeur null dans le classement est définie par SQL Server. LINQ to SQL ne change pas le classement.

Agrégats

La méthode d'agrégation (opérateur de requête standard) Sum prend la valeur zéro pour une séquence vide ou contenant uniquement des valeurs null. Dans LINQ to SQL, la sémantique de SQL reste inchangée et Sum prend la valeur null plutôt que la valeur zéro pour une séquence vide ou contenant uniquement des valeurs null.

Les limitations SQL sur les résultats intermédiaires s’appliquent aux agrégats dans LINQ to SQL. Le Sum de quantités d'entiers 32 bits n'est pas calculé à partir des résultats 64 bits. Un dépassement de capacité peut se produire pour une traduction LINQ to SQL de Sum, même si l’implémentation de l’opérateur de requête standard n’entraîne pas de dépassement de capacité pour la séquence en mémoire correspondante.

De même, la traduction LINQ to SQL de Average pour des valeurs entières est calculée comme un integer et non comme un double.

Arguments d’entité

LINQ to SQL permet d’utiliser des types d’entité dans les méthodes GroupBy et OrderBy. Dans la traduction de ces opérateurs, l’utilisation d’un argument d’un type est considérée comme équivalente à la spécification de tous les membres de ce type. Par exemple, le code suivant est é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})

Arguments égaux/comparables

L’implémentation des méthodes suivantes nécessite l’égalité des arguments :

LINQ to SQL prend en charge l’égalité et la comparaison pour les arguments plats, mais pas pour les arguments qui contiennent des séquences ou correspondent à des séquences. Un argument plat est un type qui peut être mappé à une ligne SQL. Une projection d'un ou de plusieurs types d'entité qui peuvent être déterminés statiquement comme ne contenant pas de séquence est considérée comme un argument plat.

Voici des exemples d’arguments plats :

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})

Voici des exemples d’arguments non plats (hiérarchiques) :

// 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)

Traduction de fonctions Visual Basic

Les fonctions d'assistance suivantes utilisées par le compilateur Visual Basic sont traduites en opérateurs et en fonctions SQL correspondants :

  • CompareString

  • DateTime.Compare

  • Decimal.Compare

  • IIf (in Microsoft.VisualBasic.Interaction)

Méthodes de conversion :

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

Prise en charge de l'héritage

Restrictions du mappage d'héritage

Pour plus d’informations, consultez Comment : mapper des hiérarchies d’héritage.

Héritage dans les requêtes

Les casts C# sont pris en charge uniquement dans la projection. Les casts utilisés ailleurs ne sont pas traduits et sont ignorés. Hormis les noms de fonction SQL, SQL effectue uniquement l'équivalent du Convert CLR. Autrement dit, SQL peut modifier la valeur d'un type en un autre. Le cast CLR n'a pas d'équivalent car il n'existe pas de concept de réinterprétation des mêmes bits que ceux d'un autre type. C'est pourquoi un cast C# fonctionne uniquement en local. Il n'est pas mis à distance.

Les opérateurs is et as et la méthode GetType ne sont pas restreints à l'opérateur Select. Ils peuvent également être utilisés dans d'autres opérateurs de requête.

Prise en charge de SQL Server 2008

À partir du .NET Framework version 3.5 SP1, LINQ to SQL prend en charge le mappage aux nouveaux types de date et d'heure introduits avec SQL Server 2008. Cependant, il existe certaines limites concernant les opérateurs de requête LINQ to SQL pris en charge pour les valeurs mappées à ces nouveaux types.

Opérateurs de requête non pris en charge

Les opérateurs de requête suivants ne sont pas pris en charge pour les valeurs mappées aux nouveaux types de date et d'heure SQL Server : DATETIME2, DATE, TIME et DATETIMEOFFSET.

  • Aggregate

  • Average

  • LastOrDefault

  • OfType

  • Sum

Pour plus d’informations sur le mappage à ces types de date et d’heure SQL Server, consultez Mappage de type SQL-CLR.

Prise en charge de SQL Server 2005

LINQ to SQL ne prend pas en charge les fonctionnalités SQL Server 2005 suivantes :

  • Procédures stockées écrites pour le CLR SQL.

  • Type défini par l'utilisateur.

  • Fonctionnalités de requête XML.

Prise en charge de SQL Server 2000

Les limitations suivantes de SQL Server 2000 (par rapport à Microsoft SQL Server 2005) affectent la prise en charge de LINQ to SQL.

Opérateurs Cross Apply et Outer Apply

Ces opérateurs ne sont pas disponibles dans SQL Server 2000. LINQ to SQL tente une série de réécritures pour les remplacer par des jointures appropriées.

Cross Apply et Outer Apply sont générés pour les navigations dans les relations. Le jeu des requêtes pour lequel ces réécritures sont possibles n'est pas bien défini. Le jeu de requêtes minimal pris en charge pour SQL Server 2000 est donc celui qui n’implique pas de navigation dans les relations.

text / ntext

Les types de données text / ntext ne peuvent pas être utilisés dans certaines opérations de requête sur varchar(max) / nvarchar(max), qui sont prises en charge par Microsoft SQL Server 2005.

Aucune résolution n’est disponible pour cette limitation. Précisément, vous ne pouvez pas utiliser Distinct() sur des résultats contenant des membres mappés à des colonnes text ou ntext.

Comportement déclenché par les sous-requêtes

Le classeur SQL Server 2000 (via SP4) présente des spécificités qui sont déclenchées par les sous-requêtes. Le jeu de requêtes SQL qui déclenche ces spécificités n'est pas bien défini. C’est pourquoi vous ne pouvez pas définir le jeu des requêtes LINQ to SQL qui peut entraîner des exceptions SQL Server.

Opérateurs Skip et Take

Take et Skip sont soumis à certaines limites lorsqu'ils sont utilisés dans des requêtes SQL Server 2000. Pour plus d’informations, consultez « Exceptions d’ignorance (Skip) et d’acceptation (Take) dans SQL Server 2000 » dans Résolution des problèmes.

Matérialisation d'objet

La matérialisation crée des objets CLR à partir de lignes retournées par une ou plusieurs requêtes SQL.

  • Les appels suivants sont exécutés localement dans le cadre de la matérialisation :

    • Constructeurs

    • Méthodes ToString dans les projections

    • Casts de type dans les projections

  • Les méthodes qui suivent la méthode AsEnumerable sont exécutées localement. Cette méthode ne provoque pas d'exécution immédiate.

  • Vous pouvez utiliser un struct comme type de retour d'un résultat de requête ou comme membre du type de résultat. Les entités doivent être des classes. Les types anonymes sont matérialisés en instances de classe, mais les structs nommés (non entités) peuvent être utilisés dans la projection.

  • Un membre du type de retour d’un résultat de requête peut être de type IQueryable<T>. Il est matérialisé en collection locale.

  • Les méthodes suivantes provoquent la matérialisation immédiate de la séquence à laquelle les méthodes s’appliquent :

Voir aussi