Mise en forme des résultats de requête (Entity Framework)

Lorsque vous exécutez une requête, seuls les objets qui sont spécifiquement demandés dans la requête sont retournés. Par exemple, lorsqu'une requête exécutée sur le modèle de vente Adventure Works Sales Model retourne des objets Customer, par défaut, les objets connexes SalesOrderHeader ne sont pas retournés, même s'il existe une relation entre Customer et SalesOrderHeader. Ce comportement est l'assurance que l'application a toujours conscience de l'étendue des données retournées à partir d'une requête d'objet. Par défaut, les objets de relation qui représentent des associations entre différents types d'entités sont toujours retournés. Lorsque des objets sont générés selon le schéma conceptuel du modèle EDM, des propriétés de navigation sont générées pour les objets d'entité aux deux terminaisons d'une association. Ces propriétés de navigation retournent un EntityReference à la terminaison « un » d'une relation un-à-un ou plusieurs-à-un ou un EntityCollection à la terminaison « plusieurs » d'une relation un-à-plusieurs ou plusieurs-à-plusieurs. Pour plus d'informations, voir Relations du modèle Entity Data Model.

Vous pouvez composer une requête Entité SQL ou LINQ to Entities qui parcourt explicitement ces relations en utilisant des propriétés de navigation. Pour plus d'informations, voir Procédure : explorer des relations à l'aide des propriétés de navigation (Entity Framework). Toutefois, il n'est pas nécessaire de parcourir explicitement les relations de votre requête pour mettre en forme ses résultats. Il existe deux façons d'étendre les résultats d'une requête de sorte que les objets référencés soient également chargés : vous pouvez soit spécifier des chemins d'accès de requête, soit charger explicitement les objets connexes via les propriétés de navigation. Pour exercer un contrôle encore plus poussé sur les résultats, vous pouvez définir un chemin d'accès de requête et charger ensuite explicitement les seuls objets connexes sélectionnés.

Au moment de choisir l'option à utiliser, sachez qu'il y a une corrélation entre le nombre de demandes adressées à la base de données et la quantité de données retournées dans une requête unique. Les chemins d'accès de requête définissent le graphique des objets retournés par une requête. Lorsque vous définissez un chemin d'accès de requête, il suffit d'adresser une demande unique à la base de données pour que tous les objets définis par le chemin d'accès soient retournés dans un jeu de résultats unique. Le chargement explicite d'objets réclame plusieurs allers-retours à la base de données et peut nécessiter plusieurs jeux de résultats actifs, mais la quantité de données retournées se limite aux seuls objets chargés.

Définition d'un chemin d'accès de requête pour mettre en forme les résultats de requête

Pour spécifier le chemin d'accès de la requête, transmettez une représentation de chaîne du graphique d'objet à la méthode Include sur ObjectQuery. Ce chemin d'accès indique quels objets connexes retourner lors de l'exécution d'une requête d'objet. Par exemple, si vous définissez un chemin d'accès dans une requête pour des objets Contact, vous serez assuré que chaque objet connexe SalesOrderHeader et SalesOrderDetail sera retourné. Cela est démontré dans les requêtes ci-dessous qui utilisent LINQ to Entities, Entité SQL et des méthodes du Générateur de requêtes.

  • LINQ to Entities
``` vb
' Define a LINQ query with a path that returns 
' orders and items for a contact.
Dim contacts = (From contact In context.Contact _
    .Include("SalesOrderHeader.SalesOrderDetail") _
    Select contact).FirstOrDefault()
```

``` csharp
// Define a LINQ query with a path that returns 
// orders and items for a contact.
var contacts = (from contact in context.Contact
              .Include("SalesOrderHeader.SalesOrderDetail")
              select contact).FirstOrDefault();
```
  • Entité SQL
``` vb
' Define an object query with a path that returns 
' orders and items for a specific contact.              
Dim queryString As String = _
    "SELECT VALUE TOP(1) Contact FROM " + _
    "AdventureWorksEntities.Contact AS Contact"

' Define the object query with the query string.
Dim contactQuery As New ObjectQuery(Of Contact)(queryString, _
    context, MergeOption.NoTracking)

Dim contact As Contact = _
contactQuery.Include("SalesOrderHeader.SalesOrderDetail") _
    .FirstOrDefault()
```

``` csharp
// Define an object query with a path that returns 
// orders and items for a specific contact.              
string queryString =
    @"SELECT VALUE TOP(1) Contact FROM " + 
    "AdventureWorksEntities.Contact AS Contact";

// Define the object query with the query string.
ObjectQuery<Contact> contactQuery = new ObjectQuery<Contact>(queryString, 
    context, MergeOption.NoTracking);

Contact contact =
    contactQuery.Include("SalesOrderHeader.SalesOrderDetail")
    .FirstOrDefault();
```
  • Méthodes du Générateur de requêtes

    ' Create an object query with a path that returns orders and items for a contact.
    Dim contact As Contact = _
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail") _
        .FirstOrDefault()
    
    // Define an object query with a path that returns 
    // orders and items for a specific contact.
    Contact contact =
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail")
        .FirstOrDefault();
    

Au moment de définir des chemins d'accès de requête, vous devez tenir compte des points suivants :

  • Les chemins d'accès de requête peuvent être utilisés avec les méthodes du Générateur de requêtes et les requêtes LINQ.

  • Lorsque vous appelez Include, le chemin d'accès de la requête n'est valide que sur l'instance retournée de ObjectQuery. Les autres instances de ObjectQuery et le contexte d'objet lui-même ne sont pas affectés.

  • Comme Include retourne l'objet de requête, vous pouvez appeler cette méthode à plusieurs reprises sur un ObjectQuery pour inclure les objets de plusieurs relations, comme dans l'exemple suivant :

    ' Create a SalesOrderHeader query with two query paths, 
    ' one that returns order items and a second that returns the 
    ' billing and shipping addresses for each order.
    Dim query As ObjectQuery(Of SalesOrderHeader) = _
        context.SalesOrderHeader.Include("SalesOrderDetail").Include("Address")
    
    // Create a SalesOrderHeader query with two query paths, 
    // one that returns order items and a second that returns the 
    // billing and shipping addresses for each order.
    ObjectQuery<SalesOrderHeader> query =
        context.SalesOrderHeader.Include("SalesOrderDetail").Include("Address");
    
  • L'utilisation de chemins d'accès de requête peut se traduire par l'exécution de commandes complexes sur la source de données à partir de requêtes d'objet simples d'apparence. Cela s'explique par le fait qu'une ou plusieurs jointures sont nécessaires pour qu'une même requête retourne des objets connexes. Cette complexité est plus prononcée dans le cas des requêtes exécutées sur un modèle EDM complexe, par exemple, une entité avec héritage ou un chemin d'accès qui inclut des relations plusieurs-à-plusieurs. Utilisez la méthode ToTraceString pour voir la commande qui sera générée par un ObjectQuery. Pour plus d'informations, voir Requêtes d'objet (Entity Framework). Lorsqu'un chemin d'accès de requête comprend un trop grand nombre d'objets connexes ou que les objets contiennent une trop grande quantité de données de ligne, la source de données risque de ne pas être en mesure de faire aboutir la requête. Cela se produit si la requête a besoin d'un stockage temporaire intermédiaire qui dépasse les capacités de la source de données. En pareil cas, vous pouvez réduire la complexité de la requête de source de données en chargeant explicitement les objets connexes.

Pour plus d'informations, voir Procédure : utiliser des chemins d'accès de requête pour personnaliser des résultats (Entity Framework).

Chargement explicite d'objets connexes

Pour charger explicitement des objets connexes, vous devez appeler la méthode Load sur la terminaison connexe retournée par la propriété de navigation. Dans le cas d'une relation un-à-plusieurs, appelez la méthode Load sur EntityCollection ; dans le cas d'une relation un-à-un, appelez la méthode Load sur EntityReference. Cela charge les données d'objets connexes dans le contexte d'objet. Lorsqu'une requête retourne une collection d'objets, vous pouvez énumérer la collection et appeler la méthode Load pour charger les objets connexes pour chaque objet de la collection, par exemple, chaque objet SalesOrderDetail appartenant à un objet SalesOrderHeader. Dans l'exemple suivant, les objets SalesOrderDetail sont explicitement chargés pour l'objet SalesOrderHeader spécifié :

' Load the items for the order if not already loaded.
If Not order.SalesOrderDetail.IsLoaded Then
    order.SalesOrderDetail.Load()
End If
// Load the items for the order if not already loaded.
if (!order.SalesOrderDetail.IsLoaded)
{
    order.SalesOrderDetail.Load();
}
NoteRemarque

Lorsque vous appelez la méthode Load lors d'une énumération foreach (C#) ou For Each (Visual Basic), Object Services essaie d'ouvrir un nouveau lecteur de données. Cette opération échoue sauf si vous avez activé les ensembles de résultats actifs multiples (MARS) en spécifiant multipleactiveresultsets=true dans la chaîne de connexion. Pour plus d'informations, voir Utilisation des ensembles de résultats actifs multiples (MARS) sur MSDN. Vous pouvez également charger le résultat de la requête dans une collection List, ce qui a pour effet de fermer le lecteur de données et de vous permettre d'énumérer la collection pour charger les objets référencés.

Pour plus d'informations, voir Procédure : charger explicitement des objets connexes (Entity Framework).

Interrogation des objets connexes

Étant donné que la classe EntityCollection implémente l'interface IEnumerable, vous pouvez utiliser LINQ pour interroger la collection d'objets chargés dans le EntityCollection retourné par une propriété de navigation. Cela vaut aussi bien pour les objets qui ont été implicitement chargés dans le contexte d'objet en spécifiant un chemin d'accès de requête que pour les objets qui ont été explicitement chargés en appelant la méthode Load.

En appelant la méthode CreateSourceQuery sur un EntityCollection, vous pouvez interroger les objets connexes sans avoir à charger préalablement les objets dans la collection. CreateSourceQuery retourne un ObjectQuery qui, une fois exécuté, retourne le même jeu d'objets que si vous appelez la méthode Load. Vous pouvez appliquer des méthodes du Générateur de requêtes à cette requête d'objet pour filtrer davantage les objets chargés dans la collection. Pour plus d'informations, voir Procédure : interroger les objets connexes d'un EntityCollection (Entity Framework).

Un ObjectQuery retourne les données EDM sous forme d'objets d'entité. Toutefois, lorsqu'une propriété de navigation est incluse dans la projection de requête, le ObjectQuery retourne un DbDataRecord imbriqué contenant les objets connexes. Pour plus d'informations, voir Procédure : explorer des relations à l'aide des propriétés de navigation (Entity Framework).

Voir aussi

Concepts

Interrogation des données sous forme d'objets (Entity Framework)
Requêtes d'objet (Entity Framework)
Méthodes du Générateur de requêtes (Entity Framework)