Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Une fois qu’une requête LINQ est créée par un utilisateur, elle est convertie en arborescence de commandes. Une arborescence de commandes est une représentation d’une requête compatible avec Entity Framework. L’arborescence de commandes est ensuite exécutée sur la source de données. Au moment de l’exécution de la requête, toutes les expressions de requête (autrement dit, tous les composants de la requête) sont évaluées, y compris celles utilisées dans la matérialisation des résultats.
À quel moment les expressions de requête sont exécutées peuvent varier. Les requêtes LINQ sont toujours exécutées lorsque la variable de requête est itérée, et non lorsque la variable de requête est créée. Il s’agit de l’exécution différée. Vous pouvez également forcer l’exécution immédiate d’une requête, ce qui est utile pour mettre en cache les résultats de la requête. Cette procédure est décrite plus loin dans cette rubrique.
Lorsqu’une requête LINQ to Entities est exécutée, certaines expressions de la requête peuvent être exécutées sur le serveur et certaines parties peuvent être exécutées localement sur le client. L’évaluation côté client d’une expression a lieu avant l’exécution de la requête sur le serveur. Si une expression est évaluée sur le client, le résultat de cette évaluation est remplacé par l’expression dans la requête et la requête est ensuite exécutée sur le serveur. Étant donné que les requêtes sont exécutées sur la source de données, la configuration de la source de données remplace le comportement spécifié dans le client. Par exemple, la gestion des valeurs Null et la précision numérique dépendent des paramètres du serveur. Toutes les exceptions levées pendant l'exécution de la requête sur le serveur sont passées directement au client.
Conseil / Astuce
Pour obtenir un résumé pratique des opérateurs de requête au format de table, qui vous permet d’identifier rapidement le comportement d’exécution d’un opérateur, consultez Classification des opérateurs de requête standard par mode d’exécution (C#) .
Exécution de requête différée
Dans une requête qui retourne une séquence de valeurs, la variable de requête elle-même ne contient jamais les résultats de la requête et stocke uniquement les commandes de requête. L’exécution de la requête est différée jusqu’à ce que la variable de requête soit itérée dans une foreach
ou For Each
boucle. Il s’agit de l’exécution différée ; autrement dit, l’exécution de la requête se produit un certain temps après la construction de la requête. Cela signifie que vous pouvez exécuter une requête aussi fréquemment que vous le souhaitez. Cela est utile lorsque, par exemple, vous disposez d’une base de données mise à jour par d’autres applications. Dans votre application, vous pouvez créer une requête pour récupérer les dernières informations et exécuter à plusieurs reprises la requête, en retournant les informations mises à jour à chaque fois.
L’exécution différée permet à plusieurs requêtes d’être combinées ou d’étendre une requête. Lorsqu’une requête est étendue, elle est modifiée pour inclure les nouvelles opérations, et l’exécution éventuelle reflète les modifications. Dans l’exemple suivant, la première requête retourne tous les produits. La deuxième requête étend la première en utilisant Where
pour retourner tous les produits de taille « L » :
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<Product> productsQuery =
from p in context.Products
select p;
IQueryable<Product> largeProducts = productsQuery.Where(p => p.Size == "L");
Console.WriteLine("Products of size 'L':");
foreach (var product in largeProducts)
{
Console.WriteLine(product.Name);
}
}
Using context As New AdventureWorksEntities()
Dim productsQuery = _
From p In context.Products _
Select p
Dim largeProducts = _
productsQuery.Where(Function(p) p.Size = "L")
Console.WriteLine("Products of size 'L':")
For Each product In largeProducts
Console.WriteLine(product.Name)
Next
End Using
Une fois qu’une requête a été exécutée, toutes les requêtes successives utilisent les opérateurs LINQ en mémoire. L'itération sur la variable de requête à l’aide d’une instruction foreach
ou For Each
, ou en appelant l’un des opérateurs de conversion LINQ, entraîne l’exécution immédiate. Ces opérateurs de conversion incluent les éléments suivants : ToList, , ToArrayToLookup, et ToDictionary.
Exécution de requête immédiate
Contrairement à l’exécution différée de requêtes qui produisent une séquence de valeurs, les requêtes qui retournent une valeur singleton sont exécutées immédiatement. Voici quelques exemples de requêtes singleton : Average, Count, Firstet Max. Celles-ci s’exécutent immédiatement, car la requête doit produire une séquence pour calculer le résultat singleton. Vous pouvez également forcer l’exécution immédiate. Cela est utile lorsque vous souhaitez mettre en cache les résultats d’une requête. Pour forcer l’exécution immédiate d’une requête qui ne produit pas de valeur singleton, vous pouvez appeler la ToList méthode, la ToDictionary méthode ou la ToArray méthode sur une requête ou une variable de requête. L’exemple suivant utilise la ToArray méthode pour évaluer immédiatement une séquence dans un tableau.
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
ObjectSet<Product> products = context.Products;
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);
}
}
Using context As New AdventureWorksEntities
Dim products As ObjectSet(Of Product) = context.Products
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
Vous pouvez également forcer l’exécution en plaçant la boucle foreach
ou For Each
immédiatement après l’expression de requête, mais en appelant ToList ou ToArray, vous mettez en cache toutes les données dans un objet de collection unique.
Exécution sur les magasins
En général, les expressions dans LINQ to Entities sont évaluées sur le serveur et le comportement de l’expression ne doit pas être attendu pour suivre la sémantique CLR (Common Language Runtime), mais celles de la source de données. Toutefois, il existe des exceptions à ce problème, par exemple lorsque l’expression est exécutée sur le client. Cela peut entraîner des résultats inattendus, par exemple lorsque le serveur et le client se trouvent dans différents fuseaux horaires.
Certaines expressions de la requête peuvent être exécutées sur le client. En règle générale, la plupart des exécutions de requêtes sont censées se produire sur le serveur. Outre les méthodes exécutées sur des éléments de requête mappés à la source de données, il existe souvent des expressions dans la requête qui peuvent être exécutées localement. L’exécution locale d’une expression de requête génère une valeur qui peut être utilisée dans l’exécution de la requête ou la construction du résultat.
Certaines opérations sont toujours exécutées sur le client, notamment la liaison de valeurs, les sous-expressions, les sous-requêtes de clôtures et la matérialisation d'objets dans les résultats de requête. L’effet net de ceci est que ces éléments (par exemple, les valeurs de paramètre) ne peuvent pas être mis à jour pendant l’exécution. Les types anonymes peuvent être construits directement dans la source de données, mais il ne faut pas supposer qu'ils le feront. Les regroupements inline peuvent également être construits dans la source de données, mais cela ne doit pas être pris en compte dans chaque instance. En général, il est préférable de ne pas faire d’hypothèses sur ce qui est construit sur le serveur.
Cette section décrit les scénarios dans lesquels le code est exécuté localement sur le client. Pour plus d’informations sur les types d’expressions exécutés localement, consultez Expressions dans les requêtes LINQ to Entities.
Littéraux et paramètres
Les variables locales, telles que la orderID
variable de l’exemple suivant, sont évaluées sur le client.
int orderID = 51987;
IQueryable<SalesOrderHeader> salesInfo =
from s in context.SalesOrderHeaders
where s.SalesOrderID == orderID
select s;
Dim orderID As Integer = 51987
Dim salesInfo = _
From s In context.SalesOrderHeaders _
Where s.SalesOrderID = orderID _
Select s
Les paramètres de méthode sont également évalués sur le client. Le orderID
paramètre passé dans la MethodParameterExample
méthode, ci-dessous, est un exemple.
public static void MethodParameterExample(int orderID)
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<SalesOrderHeader> salesInfo =
from s in context.SalesOrderHeaders
where s.SalesOrderID == orderID
select s;
foreach (SalesOrderHeader sale in salesInfo)
{
Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue);
}
}
}
Function MethodParameterExample(ByVal orderID As Integer)
Using context As New AdventureWorksEntities()
Dim salesInfo = _
From s In context.SalesOrderHeaders _
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
Conversion des littéraux sur le client
Le cast d'une valeur null
en type CLR est exécutée sur le client :
IQueryable<Contact> query =
from c in context.Contacts
where c.EmailAddress == (string)null
select c;
Dim query = _
From c In context.Contacts _
Where c.EmailAddress = CType(Nothing, String) _
Select c
Le cast en type, tel qu'un type Decimal Nullable, est exécutée sur le client :
var weight = (decimal?)23.77;
IQueryable<Product> query =
from product in context.Products
where product.Weight == weight
select product;
Dim weight = CType(23.77, Decimal?)
Dim query = _
From product In context.Products _
Where product.Weight = weight _
Select product
Constructeurs pour littéraux
Les nouveaux types CLR qui peuvent être mappés aux types de modèles conceptuels sont exécutés sur le client :
var weight = new decimal(23.77);
IQueryable<Product> query =
from product in context.Products
where product.Weight == weight
select product;
Dim weight = New Decimal(23.77)
Dim query = _
From product In context.Products _
Where product.Weight = weight _
Select product
Les nouveaux tableaux sont également exécutés sur le client.
Exceptions de magasin
Toutes les erreurs de magasin rencontrées pendant l’exécution de la requête sont transmises au client et ne sont pas mappées ou gérées.
Configuration de la boutique
Lorsque la requête s’exécute sur le magasin, la configuration du magasin remplace tous les comportements du client et la sémantique de magasin est exprimée pour toutes les opérations et expressions. Cela peut entraîner une différence de comportement entre le CLR et l'exécution en magasin dans des domaines tels que les comparaisons nulles, l'ordre des GUID, la précision et l'exactitude des opérations impliquant des types de données non précis (tels que des types à virgule flottante ou DateTime) et les opérations sur les chaînes. Il est important de garder cela à l’esprit lors de l’examen des résultats de la requête.
Par exemple, voici quelques différences de comportement entre le CLR et SQL Server :
SQL Server commande des GUID différemment du CLR.
Il peut également y avoir des différences dans la précision des résultats lors du traitement du type décimal sur SQL Server. Cela est dû aux exigences de précision fixes du type décimal SQL Server. Par exemple, la moyenne des Decimal valeurs 0,0, 0,0 et 1,0 est 0,3333333333333333333333333333333 en mémoire du client, mais serait 0,333333 dans la base de données (en fonction de la précision par défaut pour le type décimal de SQL Server).
Certaines opérations de comparaison de chaînes sont également gérées différemment dans SQL Server que dans le CLR. Le comportement de comparaison des chaînes dépend des paramètres de classement sur le serveur.
Les appels de fonction ou de méthode, lorsqu’ils sont inclus dans une requête LINQ to Entities, sont mappés à des fonctions canoniques dans Entity Framework, qui sont ensuite traduites en Transact-SQL et exécutées sur la base de données SQL Server. Il existe des cas où le comportement de ces fonctions mappées peut différer de l’implémentation dans les bibliothèques de classes de base. Par exemple, l'appel des méthodes Contains, StartsWith et EndsWith avec une chaîne vide en tant que paramètre retourne
true
lorsqu'il est exécuté dans le CLR, mais retournefalse
lorsqu'il est exécuté dans SQL Server. La EndsWith méthode peut également retourner des résultats différents, car SQL Server considère que deux chaînes sont égales si elles diffèrent uniquement dans l’espace blanc de fin, tandis que le CLR les considère comme non égaux. Ceci est illustré par l’exemple suivant :
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<string> query = from p in context.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 "));
}
Using context As New AdventureWorksEntities()
Dim query = _
From p In context.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