Partager via


Chargement d’entités associées

Entity Framework propose trois façons de charger des données associées : le chargement hâtif, le chargement différé et le chargement explicite. Les techniques présentées dans cette rubrique s’appliquent également aux modèles créés avec Code First et EF Designer.

Chargement hâtif

Le chargement hâtif est un processus dans lequel une requête d’un seul type d’entité charge également des entités associées dans le cadre de la requête. Le chargement hâtif est réalisé en utilisant la méthode Include. Par exemple, les requêtes suivantes chargent des blogs et tous les billets liés à chaque blog.

using (var context = new BloggingContext())
{
    // Load all blogs and related posts.
    var blogs1 = context.Blogs
                        .Include(b => b.Posts)
                        .ToList();

    // Load one blog and its related posts.
    var blog1 = context.Blogs
                       .Where(b => b.Name == "ADO.NET Blog")
                       .Include(b => b.Posts)
                       .FirstOrDefault();

    // Load all blogs and related posts
    // using a string to specify the relationship.
    var blogs2 = context.Blogs
                        .Include("Posts")
                        .ToList();

    // Load one blog and its related posts
    // using a string to specify the relationship.
    var blog2 = context.Blogs
                       .Where(b => b.Name == "ADO.NET Blog")
                       .Include("Posts")
                       .FirstOrDefault();
}

Remarque

Include est une méthode d’extension dans l’espace de noms System.Data.Entity. Veillez donc à utiliser cet espace de noms.

Chargement hâtif de plusieurs niveaux

Il est également possible de charger hâtivement plusieurs niveaux d’entités associées. Les requêtes suivantes montrent des exemples de ce processus pour les propriétés de navigation de collection et de référence.

using (var context = new BloggingContext())
{
    // Load all blogs, all related posts, and all related comments.
    var blogs1 = context.Blogs
                        .Include(b => b.Posts.Select(p => p.Comments))
                        .ToList();

    // Load all users, their related profiles, and related avatar.
    var users1 = context.Users
                        .Include(u => u.Profile.Avatar)
                        .ToList();

    // Load all blogs, all related posts, and all related comments  
    // using a string to specify the relationships.
    var blogs2 = context.Blogs
                        .Include("Posts.Comments")
                        .ToList();

    // Load all users, their related profiles, and related avatar  
    // using a string to specify the relationships.
    var users2 = context.Users
                        .Include("Profile.Avatar")
                        .ToList();
}

Remarque

Pour le moment, il est impossible de filtrer les entités associées qui sont chargées. Include rend toujours toutes les entités associées.

Chargement différé

Le chargement différé est le processus dans lequel une entité ou une collection d’entités est automatiquement chargée à partir de la base de données dès la première fois qu’une propriété faisant référence à l’entité/aux entités est accessible. Lorsque vous utilisez des types d’entités POCO, le chargement différé est réalisé en créant des instances des types de proxies dérivés, puis en écrasant les propriétés virtuelles pour ajouter le crochet de chargement. Par exemple, lors de l’utilisation de la classe d’entité Blog définie ci-dessous, les billets (posts) associés sont chargés la première fois que la propriété de navigation Posts est consultée :

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public string Tags { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Désactiver le chargement différé pour la sérialisation

Le chargement différé et la sérialisation se mélangent mal. Si vous ne faites pas attention, vous pouvez finir par interroger toute votre base de données simplement parce que le chargement différé est activé. La plupart des sérialiseurs fonctionnent en accédant à chaque propriété sur une instance de type. L’accès aux propriétés déclenche le chargement différé. Cela signifie que davantage d’entités sont sérialisées. Des propriétés sont consultées sur ces entités et encore plus d’entités sont chargées. Il est recommandé de désactiver le chargement différé avant de sérialiser une entité. Les sections suivantes montrent comment effectuer cette opération.

Désactivation du chargement différé pour des propriétés spécifiques de navigation

Le chargement différé de la collection Posts peut être désactivé en rendant la propriété Posts non virtuelle :

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public string Tags { get; set; }

    public ICollection<Post> Posts { get; set; }
}

Le chargement de la collection Posts peut toujours être effectué à l’aide du chargement hâtif (voir Chargement hâtif ci-dessus) ou de la méthode Load (voir Chargement explicite ci-dessous).

Désactiver le chargement différé pour toutes les entités

Le chargement différé peut être désactivé pour toutes les entités dans le contexte en définissant un indicateur sur la propriété Configuration. Par exemple :

public class BloggingContext : DbContext
{
    public BloggingContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}

Le chargement d’entités associées peut toujours être effectué à l’aide du chargement hâtif (voir Chargement hâtif ci-dessus) ou de la méthode Load (voir Chargement explicite ci-dessous).

Chargement explicite

Même si le chargement différé est désactivé, il est toujours possible de charger des entités associées de façon différée. Toutefois, cela doit être effectué à l’aide d’un appel explicite. Pour ce faire, vous utilisez la méthode Load sur l’entrée de l’entité associée. Par exemple :

using (var context = new BloggingContext())
{
    var post = context.Posts.Find(2);

    // Load the blog related to a given post.
    context.Entry(post).Reference(p => p.Blog).Load();

    // Load the blog related to a given post using a string.
    context.Entry(post).Reference("Blog").Load();

    var blog = context.Blogs.Find(1);

    // Load the posts related to a given blog.
    context.Entry(blog).Collection(p => p.Posts).Load();

    // Load the posts related to a given blog
    // using a string to specify the relationship.
    context.Entry(blog).Collection("Posts").Load();
}

Remarque

La méthode Reference doit être utilisée lorsqu’une entité a une propriété de navigation vers une autre entité unique. En revanche, la méthode Collection doit être utilisée lorsqu’une entité a une propriété de navigation vers une collection d’autres entités.

La méthode Query fournit un accès à la requête sous-jacente utilisée par Entity Framework lors du chargement d’entités associées. Vous pouvez alors utiliser LINQ pour appliquer des filtres à la requête avant de l’exécuter avec un appel à une méthode d’extension LINQ comme ToList, Load, etc. La méthode Query peut être utilisée avec les propriétés de navigation de référence et de collection, mais elle est surtout utile pour les collections où elle peut être utilisée pour charger uniquement une partie de la collection. Par exemple :

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Load the posts with the 'entity-framework' tag related to a given blog.
    context.Entry(blog)
           .Collection(b => b.Posts)
           .Query()
           .Where(p => p.Tags.Contains("entity-framework"))
           .Load();

    // Load the posts with the 'entity-framework' tag related to a given blog
    // using a string to specify the relationship.
    context.Entry(blog)
           .Collection("Posts")
           .Query()
           .Where(p => p.Tags.Contains("entity-framework"))
           .Load();
}

Lorsque vous utilisez la méthode Query, il est généralement préférable de désactiver le chargement différé pour la propriété de navigation. Sinon, la collection entière peut être chargée automatiquement par le mécanisme de chargement différé avant ou après l’exécution de la requête filtrée.

Remarque

Bien que la relation puisse être spécifiée en tant que chaîne au lieu d’une expression lambda, l’IQueryable retourné n’est pas générique lorsqu’une chaîne est utilisée. La méthode Cast est donc généralement nécessaire avant que l’IQueryable puisse avoir une quelconque utilité.

Il est parfois utile de savoir combien d’entités sont associées à une autre entité de la base de données sans s’exposer aux coûts de chargement de toutes ces entités. La méthode Query avec la méthode LINQ Count peut être utilisée pour effectuer cette opération. Par exemple :

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Count how many posts the blog has.
    var postCount = context.Entry(blog)
                           .Collection(b => b.Posts)
                           .Query()
                           .Count();
}