Share via


Exécution de requête

Après avoir été créée par un utilisateur, une requête LINQ est convertie en arborescence de commandes. Une arborescence de commandes est une représentation de requête compatible avec Entity Framework. L'arborescence de requêtes est ensuite exécutée sur la source de données. Pendant l'exécution de la requête, toutes les expressions de la requête (c'est-à-dire, toutes ses composantes) sont évaluées, y compris les expressions utilisées dans la matérialisation des résultats.

Le moment où les expressions d'une requête sont exécutées peut varier. Les requêtes LINQ sont toujours exécutées lorsque la variable de requête fait l'objet d'une itération, et non au moment où elle est créée. On parle alors d'exécution différée. Vous pouvez également forcer l'exécution immédiate de la requête, ce qui est utile pour mettre en cache les résultats de la requête. Ce sujet est abordé plus loin dans cette rubrique.

Lorsqu'une requête LINQ to Entities est exécutée, il est possible que certaines expressions de la requête soient exécutées sur le serveur et que certaines parties soient 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 remplace l'expression de 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 substitue 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.

Exécution différée

La variable de requête proprement dite ne stocke les commandes de requête que si la requête est conçue pour retourner une séquence de valeurs. Si la requête ne contient pas de méthode qui déclenche une exécution immédiate, l'exécution effective de la requête est différée jusqu'à ce que vous parcouriez la variable de requête dans une boucle foreach ou For Each. La requête est exécutée sur le serveur chaque fois que vous parcourez la variable de requête dans une boucle foreach ou For Each. On parle alors d'exécution différée. L'exécution différée permet de combiner plusieurs requêtes ou d'étendre une requête. Dans ce dernier cas, la requête est modifiée de manière à inclure les nouvelles opérations, et l'exécution finale reflète les modifications. La variable de requête proprement dite ne conserve jamais les résultats de la requête. Cela signifie que vous pouvez exécuter une requête aussi souvent que vous le voulez. Par exemple, vous disposez peut-être d'une base de données qui est constamment mise à jour par une application distincte. Vous pouvez créer dans cette dernière une requête dans le but d'extraire les données les plus récentes, et vous pouvez l'exécuter à intervalle régulier. Vous obtiendrez à chaque fois des résultats différents.

Les requêtes LINQ to Entities sont converties en arborescences de commandes dans Entity Framework et exécutées sur la source de données lorsque les résultats font l'objet d'une itération. À ce stade, les échecs de conversion provoquent des exceptions qui sont transmises au client.

Exécution immédiate

Contrairement à l'exécution différée des requêtes qui produisent une séquence de valeurs, les requêtes qui retournent une valeur singleton sont exécutées immédiatement. Les requêtes Average, Count, First et Max en sont quelques exemples. Elles s'exécutent immédiatement parce que la requête doit produire une séquence pour calculer le résultat singleton. Vous pouvez également forcer l'exécution immédiate. Cela peut s'avérer 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 méthode ToList, ToDictionary ou ToArray sur une requête ou une variable de requête. L'exemple ci-dessous utilise la méthode ToArray pour évaluer immédiatement une séquence dans un tableau.

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

Vous pouvez également forcer l'exécution en plaçant la boucle foreach ou For Each de suite après l'expression de requête, mais en appelant ToList ou ToArray, vous mettez en cache toutes les données contenues dans un objet de collection unique.

Exécution sur les magasins

En règle générale, les expressions dans LINQ to Entities sont évaluées sur le serveur, et le comportement de l'expression n'est pas censé suivre la sémantique CLR (Common Language Runtime), mais celle de la source de données. Toutefois, il y a des exceptions à cette règle, notamment lorsque l'expression est exécutée sur le client. Cela peut donner lieu à des résultats inattendus, par exemple lorsque le serveur et le client sont situés dans des fuseaux horaires différents.

Il est possible que certaines expressions de la requête soient exécutées sur le client. En général, l'exécution de la requête est supposée se produire en grande partie sur le serveur. En dehors des méthodes exécutées sur des éléments de requête mappés à la source de données, il existe souvent, dans la requête, des expressions 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 dans 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. En conséquence de quoi, ces éléments (par exemple, les valeurs de paramètres) ne peuvent pas être mis à jour pendant l'exécution. Des types anonymes peuvent être construits par incorporation dans la source de données, mais ils ne sont pas censés l'être. Des regroupements inline peuvent être construits dans la source de données, mais cela n'est pas systématique. En règle générale, il est préférable de ne pas faire de suppositions 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ées localement, voir Expressions dans les requêtes LINQ to Entities.

Littéraux et paramètres

Les variables locales, telles que la variable orderID de l'exemple suivant, sont évaluées sur le client.

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;

Les paramètres de méthode sont également évalués sur le client. Le paramètre orderID passé dans la méthode MethodParameterExample ci-dessous en est l'illustration.

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

Conversion des littéraux sur le client

La conversion d'une valeur null dans un type CLR est exécutée sur le client :

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;

La conversion dans un type, telle qu'un objet Decimal nullable, est exécutée sur le client :

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;

Constructeurs pour littéraux

Les nouveaux types CLR qui peuvent être mappés aux types EDM sont exécutés sur le client :

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;

Les nouveaux tableaux sont également exécutés sur le client.

Exceptions de magasin

Les erreurs de magasin rencontrées lors de l'exécution d'une requête sont transmises au client. Elles ne sont ni mappées, ni gérées.

Configuration de magasin

Lorsque la requête s'exécute sur le magasin, la configuration de ce dernier prévaut sur tous les comportements du client, et la sémantique du magasin est exprimée pour l'ensemble des opérations et des expressions. Il peut en résulter une différence de comportement entre le CLR et l'exécution sur le magasin dans certains domaines tels que les comparaisons de valeurs Null, le tri de GUID, la précision et l'exactitude des opérations faisant intervenir des types de données non précis (par exemple, les types à virgule flottante ou DateTime) et les opérations de chaîne. Il est important de garder cela à l'esprit au moment d'examiner des résultats de requête.

Par exemple, voici quelques différences de comportement entre le CLR et SQL Server :

  • SQL Server trie les GUID de façon différente par rapport au CLR.

  • Des différences peuvent également être constatées sur le plan de la précision des résultats lorsque le type Décimal est utilisé dans SQL Server. Cela est dû aux exigences de précision fixe inhérentes au type Décimal de SQL Server. Par exemple, la moyenne des valeurs Decimal 0,0, 0,0 et 1,0 est de 0,3333333333333333333333333333 dans la mémoire du client, mais de 0,333333 dans le magasin (selon la précision par défaut du type Décimal de SQL Server).

  • De même, le traitement de certaines opérations de comparaison de chaînes est différent selon qu'elles sont traitées dans SQL Server ou dans le CLR. Le comportement en matière de comparaison de chaînes dépend des paramètres de classement du serveur.

  • Les appels de fonction ou de méthode éventuellement inclus dans une requête LINQ to Entities sont mappés aux fonctions canoniques d'Entity Framework, qui sont ensuite traduites dans Transact-SQL et exécutées sur la base de données SQL Server. Dans certains cas, le comportement affiché par ces fonctions mappées peut être différent 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 guise de paramètre retourne une valeur true en cas d'exécution dans le CLR, mais retourne une valeur false en cas d'exécution dans SQL Server. La méthode EndsWith peut également retourner des résultats différents. En effet, SQL Server considère que deux chaînes sont égales si seul un espace à droite les différencie. Or, dans ce cas, le CLR les considère différentes. Ceci est illustré dans l'exemple suivant :

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 "));

}

Voir aussi

Autres ressources

Exécution de requêtes avec LINQ to Entities