Chargement des données associéesLoading Related Data

Entity Framework Core vous permet d’utiliser les propriétés de navigation dans votre modèle pour charger des entités associées.Entity Framework Core allows you to use the navigation properties in your model to load related entities. Il existe trois modèles O/RM communs utilisés pour charger les données associées.There are three common O/RM patterns used to load related data.

  • Le Chargement hâtif signifie que les données associées sont chargées à partir de la base de données dans le cadre de la requête initiale.Eager loading means that the related data is loaded from the database as part of the initial query.
  • Le Chargement explicite signifie que les données associées sont explicitement chargées à partir de la base de données à un moment ultérieur.Explicit loading means that the related data is explicitly loaded from the database at a later time.
  • Le Chargement différé signifie que les données associées sont chargées de façon transparente à partir de la base de données lors de l’accès à la propriété de navigation.Lazy loading means that the related data is transparently loaded from the database when the navigation property is accessed.

Conseil

Vous pouvez afficher cet exemple sur GitHub.You can view this article's sample on GitHub.

Chargement hâtifEager loading

Vous pouvez utiliser la méthode Include pour spécifier les données associées à inclure dans les résultats de la requête.You can use the Include method to specify related data to be included in query results. Dans l’exemple suivant, les blogs retournés dans les résultats auront leurs propriétés Posts remplies avec les billets associés.In the following example, the blogs that are returned in the results will have their Posts property populated with the related posts.

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.Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. 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.So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

Vous pouvez inclure des données associées provenant de plusieurs relations dans une seule requête.You can include related data from multiple relationships in a single query.

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

Inclusion de plusieurs niveauxIncluding multiple levels

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.You can drill down through relationships to include multiple levels of related data using the ThenInclude method. L’exemple suivant charge tous les blogs, leurs messages associés et l’auteur de chaque publication.The following example loads all blogs, their related posts, and the author of each post.

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

Note

Les versions actuelles de Visual Studio offrent des options de saisie semi-automatique de code incorrectes et peuvent provoquer le marquage d’expressions correctes avec des erreurs de syntaxe lorsque vous utilisez la méthode ThenInclude après une propriété de navigation de collection.Current versions of Visual Studio offer incorrect code completion options and can cause correct expressions to be flagged with syntax errors when using the ThenInclude method after a collection navigation property. Cela est causé par un bogue d’IntelliSense suivi sur https://github.com/dotnet/roslyn/issues/8237.This is a symptom of an IntelliSense bug tracked at https://github.com/dotnet/roslyn/issues/8237. Il est possible d’ignorer ces fausses erreurs de syntaxe tant que le code est correct et qu’il peut être compilé avec succès.It is safe to ignore these spurious syntax errors as long as the code is correct and can be compiled successfully.

Vous pouvez enchaîner plusieurs appels à ThenInclude pour continuer à inclure les niveaux de données associées suivants.You can chain multiple calls to ThenInclude to continue including further levels of related data.

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 tout cela pour inclure les données associées de plusieurs niveaux et plusieurs racines dans la même requête.You can combine all of this to include related data from multiple levels and multiple roots in the same query.

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.You may want to include multiple related entities for one of the entities that is being included. Par exemple, lors de l’interrogation des Blogs, vous incluez Posts, puis souhaitez inclure à la fois les Author et les Tags des Posts.For example, when querying Blogs, you include Posts and then want to include both the Author and Tags of the Posts. Pour ce faire, vous devez spécifier chaque chemin d’accès à inclure à partir de la racine.To do this, you need to specify each include path starting at the root. Par exemple : Blog -> Posts -> Author et Blog -> Posts -> Tags.For example, Blog -> Posts -> Author and Blog -> Posts -> Tags. Cela ne signifie pas que vous obtiendrez des jointures redondantes. Dans la plupart des cas, EF consolide les jointures lors de la génération du SQL.This does not mean you will get redundant joins, in most cases EF will consolidate the joins when generating 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();
}

Inclure des types dérivésInclude on derived types

Vous pouvez inclure des données associées provenant de navigations définies uniquement sur un type dérivé à l’aide de Include et ThenInclude.You can include related data from navigations defined only on a derived type using Include and ThenInclude.

En partant du modèle suivant :Given the following model:

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 d’un certain nombre de modèles :Contents of School navigation of all People who are Students can be eagerly loaded using a number of patterns:

  • utilisation du forçage de typeusing cast

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

    context.People.Include(person => (person as Student).School).ToList()
    
  • utilisation de la surcharge de Include qui accepte les paramètres de type stringusing overload of Include that takes parameter of type string

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

Inclusions ignoréesIgnored includes

Si vous modifiez la requête afin qu’elle ne renvoie plus les instances du type d’entité par lequel la requête a commencé, les opérateurs include sont ignorés.If you change the query so that it no longer returns instances of the entity type that the query began with, then the include operators are ignored.

Dans l’exemple suivant, les opérateurs include sont basés sur le Blog, puis l’opérateur Select est utilisé pour modifier la requête pour retourner un type anonyme.In the following example, the include operators are based on the Blog, but then the Select operator is used to change the query to return an anonymous type. Dans ce cas, les opérateurs include n’ont aucun effet.In this case, the include operators have no effect.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .Select(blog => new
        {
            Id = blog.BlogId,
            Url = blog.Url
        })
        .ToList();
}

Par défaut, EF Core consigne un avertissement lorsque les opérateurs include sont ignorés.By default, EF Core will log a warning when include operators are ignored. Consultez Journalisation pour plus d’informations sur l’affichage de la sortie de la journalisation.See Logging for more information on viewing logging output. Vous pouvez modifier le comportement lorsqu’un opérateur include est ignoré, entre journaliser ou ne rien faire.You can change the behavior when an include operator is ignored to either throw or do nothing. Cette opération est effectuée lors de la définition des options pour votre contexte, généralement dans DbContext.OnConfiguring, ou Startup.cs si vous utilisez ASP.NET Core.This is done when setting up the options for your context - typically in DbContext.OnConfiguring, or in Startup.cs if you are using ASP.NET Core.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;ConnectRetryCount=0")
        .ConfigureWarnings(warnings => warnings.Throw(CoreEventId.IncludeIgnoredWarning));
}

Chargement expliciteExplicit loading

Note

Cette fonctionnalité a été introduite dans EF Core 1.1.This feature was introduced in EF Core 1.1.

Vous pouvez charger explicitement une propriété de navigation via l’API DbContext.Entry(...).You can explicitly load a navigation property via the DbContext.Entry(...) API.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    context.Entry(blog)
        .Collection(b => b.Posts)
        .Load();

    context.Entry(blog)
        .Reference(b => b.Owner)
        .Load();
}

Vous pouvez également explicitement charger une propriété de navigation en exécutant une requête distincte qui retourne les entités associées.You can also explicitly load a navigation property by executing a separate query that returns the related entities. Si le suivi des modifications est activé, lors du chargement d’une entité, EF Core définit automatiquement les propriétés de navigation de l’entité qui vient d’être chargée pour faire référence à toutes les entités déjà chargées et définir les propriétés de navigation des entités déjà chargées pour faire référence à l’entité qui vient d’être chargée.If change tracking is enabled, then when loading an entity, EF Core will automatically set the navigation properties of the newly-loaded entitiy to refer to any entities already loaded, and set the navigation properties of the already-loaded entities to refer to the newly-loaded entity.

Vous pouvez également obtenir une requête LINQ qui représente le contenu d’une propriété de navigation.You can also get a LINQ query that represents the contents of a navigation property.

Cela vous permet d’effectuer des opérations telles que l’exécution d’un opérateur d’agrégation sur les entités associées sans les charger dans la mémoire.This allows you to do things such as running an aggregate operator over the related entities without loading them into memory.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    var postCount = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Count();
}

Vous pouvez également filtrer les entités associées qui sont chargées en mémoire.You can also filter which related entities are loaded into memory.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    var goodPosts = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Where(p => p.Rating > 3)
        .ToList();
}

Chargement différéLazy loading

Note

Cette fonctionnalité a été introduite dans EF Core 2.1.This feature was introduced in EF Core 2.1.

La façon la plus simple d’utiliser le chargement différé est d’installer le package Microsoft.EntityFrameworkCore.Proxies et de l’activer avec un appel à UseLazyLoadingProxies.The simplest way to use lazy-loading is by installing the Microsoft.EntityFrameworkCore.Proxies package and enabling it with a call to UseLazyLoadingProxies. Exemple :For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseLazyLoadingProxies()
        .UseSqlServer(myConnectionString);

Ou bien, lors de l’utilisation d’AddDbContext :Or when using AddDbContext:

.AddDbContext<BloggingContext>(
    b => b.UseLazyLoadingProxies()
          .UseSqlServer(myConnectionString));

EF Core active ensuite le chargement différé pour n’importe quelle propriété de navigation qui peut être substituée, c’est-à-dire qui doit être virtual et sur une classe qui peut être héritée.EF Core will then enable lazy loading for any navigation property that can be overridden--that is, it must be virtual and on a class that can be inherited from. Par exemple, dans les entités suivantes, les propriétés de navigation Post.Blog et Blog.Posts seront chargées en différé.For example, in the following entities, the Post.Blog and Blog.Posts navigation properties will be lazy-loaded.

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

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

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public virtual Blog Blog { get; set; }
}

Chargement différé sans proxyLazy loading without proxies

Les proxys à chargement différé fonctionnent en injectant le service ILazyLoader dans une entité, comme décrit dans Constructeurs de type d’entité.Lazy-loading proxies work by injecting the ILazyLoader service into an entity, as described in Entity Type Constructors. Exemple :For example:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

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

    public ICollection<Post> Posts
    {
        get => LazyLoader.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader.Load(this, ref _blog);
        set => _blog = value;
    }
}

Il n’est pas nécessaire que l’héritage des types d’entité à partir des propriétés de navigation soit virtuel, et cela permet aux instances d’entité créées avec new d’être chargées en différé une fois jointes à un contexte.This doesn't require entity types to be inherited from or navigation properties to be virtual, and allows entity instances created with new to lazy-load once attached to a context. Toutefois, il requiert une référence au service ILazyLoader, qui est défini dans le package Microsoft.EntityFrameworkCore.Abstractions.However, it requires a reference to the ILazyLoader service, which is defined in the Microsoft.EntityFrameworkCore.Abstractions package. Ce package contient un ensemble minimal de types, de sorte que dépendre de celui-ci n’a qu’un impact très limité.This package contains a minimal set of types so that there is very little impact in depending on it. Toutefois, pour éviter complètement de dépendre des packages EF Core dans les types d’entité, il est possible d’injecter la méthode ILazyLoader.Load en tant que délégué.However, to completely avoid depending on any EF Core packages in the entity types, it is possible to inject the ILazyLoader.Load method as a delegate. Exemple :For example:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

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

    public ICollection<Post> Posts
    {
        get => LazyLoader.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader.Load(this, ref _blog);
        set => _blog = value;
    }
}

Le code ci-dessus utilise une méthode d'extension Load pour rendre l’utilisation du délégué un peu plus propre :The code above uses a Load extension method to make using the delegate a bit cleaner:

public static class PocoLoadingExtensions
{
    public static TRelated Load<TRelated>(
        this Action<object, string> loader,
        object entity,
        ref TRelated navigationField,
        [CallerMemberName] string navigationName = null)
        where TRelated : class
    {
        loader?.Invoke(entity, navigationName);

        return navigationField;
    }
}

Note

Le paramètre de constructeur pour le délégué à chargement différé doit être appelé « lazyLoader ».The constructor parameter for the lazy-loading delegate must be called "lazyLoader". La configuration permettant d’utiliser un nom différent est prévue pour une version ultérieure.Configuration to use a different name than this is planned for a future release.

Étant donné qu’EF Core corrigera automatiquement les propriétés de navigation, vous pouvez vous retrouver avec des cycles dans votre graphique d’objets.Because EF Core will automatically fix-up navigation properties, you can end up with cycles in your object graph. Par exemple, le chargement d’un blog et de ses billets associés aboutit à un objet de blog qui fait référence à une collection de billets.For example, loading a blog and its related posts will result in a blog object that references a collection of posts. Chacun de ces billets aura une référence vers le blog.Each of those posts will have a reference back to the blog.

Certaines infrastructures de sérialisation n’autorisent pas de tels cycles.Some serialization frameworks do not allow such cycles. Par exemple, Json.NET lève l’exception suivante si un cycle est rencontré.For example, Json.NET will throw the following exception if a cycle is encountered.

Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Blog' with type 'MyApplication.Models.Blog'.Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Blog' with type 'MyApplication.Models.Blog'.

Si vous utilisez ASP.NET Core, vous pouvez configurer Json.NET pour ignorer les cycles qu’il trouve dans le graphique d’objets.If you are using ASP.NET Core, you can configure Json.NET to ignore cycles that it finds in the object graph. Cette opération est effectuée dans la méthode ConfigureServices(...) de Startup.cs.This is done in the ConfigureServices(...) method in Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddMvc()
        .AddJsonOptions(
            options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
        );

    ...
}