Chargement hâtif de données connexes

Chargement hâtif

Vous pouvez utiliser la méthode Include pour spécifier les données associées à inclure dans les résultats de la requête. Dans l’exemple suivant, les blogs retournés dans les résultats auront leurs propriétés Posts remplies avec les billets associés.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ToList();
}

Conseil

Entity Framework Core corrige automatiquement les propriétés de navigation vers d’autres entités qui étaient précédemment chargées dans l’instance de contexte. Ainsi, même vous n’incluez pas explicitement si les données pour une propriété de navigation, la propriété peut toujours être renseignée si toutes ou une partie des entités associées ont été précédemment chargées.

Vous pouvez inclure des données associées provenant de plusieurs relations dans une seule requête.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .Include(blog => blog.Owner)
        .ToList();
}

Avertissement

Le chargement hâtif d’une navigation de collection dans une seule requête peut entraîner des problèmes de performances. Pour plus d’informations, consultez Requêtes simples vs. fractionnées.

Inclusion de plusieurs niveaux

Vous pouvez descendre dans la hiérarchie des relations pour inclure plusieurs niveaux de données associées à l’aide de la méthode ThenInclude. L’exemple suivant charge tous les blogs, leurs messages associés et l’auteur de chaque publication.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ToList();
}

Vous pouvez enchaîner plusieurs appels à ThenInclude pour continuer à inclure les niveaux de données associées suivants.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
        .ToList();
}

Vous pouvez combiner tous les appels pour inclure les données associées de plusieurs niveaux et plusieurs racines dans la même requête.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
        .Include(blog => blog.Owner)
        .ThenInclude(owner => owner.Photo)
        .ToList();
}

Vous pourriez souhaiter inclure plusieurs entités associées pour une entité qui est incluse. Par exemple, quand vous interrogez des Blogs, vous incluez Posts, puis souhaitez inclure à la fois les Author et les Tags des Posts. Pour inclure les deux, vous devez spécifier chaque chemin d’accès à inclure à partir de la racine. Par exemple : Blog -> Posts -> Author et Blog -> Posts -> Tags. Cela ne signifie pas que vous obtiendrez des jointures redondantes. Dans la plupart des cas, EF conmbine les jointures lors de la génération du SQL.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags)
        .ToList();
}

Conseil

Vous pouvez également charger plusieurs navigations à l’aide d’une méthode Include unique. Cela est possible pour les « chaînes » de navigation qui sont toutes des références, ou lorsqu’elles se terminent par une collection unique.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Owner.AuthoredPosts)
        .ThenInclude(post => post.Blog.Owner.Photo)
        .ToList();
}

Include filtré

Lors de l’application d’Include pour charger des données associées, vous pouvez ajouter certaines opérations énumérables à la navigation de collection incluse, ce qui permet de filtrer et de trier les résultats.

Les opérations prises en charge sont les suivantes : Where, OrderBy, OrderByDescending, ThenBy, ThenByDescending, Skipet Take.

Ces opérations doivent être appliquées sur la navigation de collection dans l’expression lambda passée à la méthode Include, comme illustré dans l’exemple ci-dessous :

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToList();
}

Chaque navigation incluse n’autorise qu’un seul ensemble d’opérations de filtre. Dans les cas où plusieurs opérations include sont appliquées pour une navigation de collection donnée (blog.Posts dans les exemples ci-dessous), les opérations de filtre peuvent uniquement être spécifiées sur l’une d’entre elles :

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

Au lieu de cela, des opérations identiques peuvent être appliquées pour chaque navigation qui est incluse plusieurs fois :

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

Avertissement

En cas de requêtes de suivi, les résultats d’Include filtré peuvent être inattendus en raison de la correction de la navigation. Toutes les entités pertinentes qui ont été interrogées précédemment et qui ont été stockées dans le dispositif de suivi des modifications sont présentes dans les résultats de la requête Include filtré, même si elles ne répondent pas aux exigences du filtre. Envisagez d’utiliser des requêtes NoTracking ou de recréer le DbContext lors de l’utilisation d’Include filtré dans ces situations.

Exemple :

var orders = context.Orders.Where(o => o.Id > 1000).ToList();

// customer entities will have references to all orders where Id > 1000, rather than > 5000
var filtered = context.Customers.Include(c => c.Orders.Where(o => o.Id > 5000)).ToList();

Remarque

En cas de requêtes de suivi, la navigation sur laquelle Include filtré a été appliqué est considérée comme étant chargée. Cela signifie qu’EF Core ne tente pas de recharger ses valeurs à l’aide d’un chargement explicite ou chargement différé, même si certains éléments peuvent toujours être manquants.

Inclure des types dérivés

Vous pouvez inclure des données associées provenant d’une navigation définie uniquement sur un type dérivé à l’aide de Include et ThenInclude.

En partant du modèle suivant :

public class SchoolContext : DbContext
{
    public DbSet<Person> People { get; set; }
    public DbSet<School> Schools { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<School>().HasMany(s => s.Students).WithOne(s => s.School);
    }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Student : Person
{
    public School School { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<Student> Students { get; set; }
}

Le contenu de la navigation School de toutes les personnes qui sont des étudiants peut être chargé dynamiquement à l’aide de nombreux modèles :

  • Utilisation d’un cast

    context.People.Include(person => ((Student)person).School).ToList()
    
  • Utilisation de l’opérateur as

    context.People.Include(person => (person as Student).School).ToList()
    
  • Utilisation de la surcharge de Include qui accepte les paramètres de type string

    context.People.Include("School").ToList()
    

Configuration du modèle pour les navigations avec inclusion automatique

Vous pouvez configurer une navigation dans le modèle à inclure chaque fois que l’entité est chargée à partir de la base de données à l’aide de la méthode AutoInclude. Cela a le même effet que de spécifier Include avec la navigation dans chaque requête où le type d’entité est retourné dans les résultats. L’exemple suivant montre comment configurer une navigation à inclure automatiquement.

modelBuilder.Entity<Theme>().Navigation(e => e.ColorScheme).AutoInclude();

Après la configuration ci-dessus, l’exécution d’une requête comme celle ci-dessous chargera la navigation ColorScheme pour tous les thèmes dans les résultats.

using (var context = new BloggingContext())
{
    var themes = context.Themes.ToList();
}

Cette configuration est appliquée à chaque entité retournée dans le résultat, quelle que soit la façon dont elle apparaît dans les résultats. Cela signifie que si une entité est dans le résultat en raison de l’utilisation d’une navigation, en utilisant Include sur un autre type d’entité ou une configuration d’inclusion automatique, elle chargera toutes les navigations incluses automatiquement pour elle. La même règle s’étend aux navigations configurées comme incluses automatiquement sur le type dérivé de l’entité.

Si, pour une requête particulière, vous ne souhaitez pas charger les données associées via une navigation, configurée au niveau du modèle pour être incluse automatiquement, vous pouvez utiliser la méthode IgnoreAutoIncludes dans votre requête. L’utilisation de cette méthode arrête le chargement de toutes les navigations configurées en tant qu’insertion automatique par l’utilisateur. L’exécution d’une requête comme ci-dessous renverra tous les thèmes de la base de données, mais ne chargera pas ColorScheme, même si elle est configurée comme une navigation incluse automatiquement.

using (var context = new BloggingContext())
{
    var themes = context.Themes.IgnoreAutoIncludes().ToList();
}

Remarque

Les navigations vers des types détenus sont également configurées comme incluses automatiquement par convention et l’utilisation de l’API IgnoreAutoIncludes ne les empêche pas d’être incluses. Elles seront toujours incluses dans les résultats de la requête.