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
, Skip
et 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 typestring
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.