Caricamento eager dei dati correlati

Caricamento eager

È possibile usare il metodo Include per specificare i dati correlati da includere nei risultati della query. Nell'esempio seguente la proprietà Posts dei blog restituiti nei risultati verrà popolata con i post correlati.

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

Suggerimento

Entity Framework Core correggerà automaticamente le proprietà di navigazione per qualsiasi altra entità caricata in precedenza nell'istanza di contesto. Anche se i dati per una proprietà di navigazione non vengono inclusi in modo esplicito, la proprietà può comunque essere popolata se alcune o tutte le entità correlate sono state caricate in precedenza.

È possibile includere dati correlati da più relazioni in una singola query.

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

Attenzione

Il caricamento eager di una navigazione nella raccolta in una singola query può causare problemi di prestazioni. Per altre informazioni, vedere Query singole e suddivise.

Inclusione di più livelli

È possibile eseguire il drill-down delle relazioni per includere più livelli di dati correlati tramite il metodo ThenInclude. L'esempio seguente carica tutti i blog, i post correlati e l'autore di ogni post.

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

È possibile concatenare più chiamate a ThenInclude per continuare a includere ulteriori livelli di dati correlati.

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

È possibile combinare tutte le chiamate per includere i dati correlati da più livelli e più radici nella stessa 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();
}

È possibile che si vogliano includere più entità correlate per una delle entità incluse. Quando ad esempio si eseguono query per Blogs, è necessario includere Posts e poi si può anche decidere di includere Author e Tags per Posts. Per includere entrambi, è necessario specificare ogni percorso di inclusione a partire dalla radice. Ad esempio, Blog -> Posts -> Author e Blog -> Posts -> Tags. Non significa che si otterranno join ridondanti; nella maggior parte dei casi Ef combina i join durante la generazione di 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();
}

Suggerimento

È anche possibile caricare più spostamenti usando un singolo Include metodo. Ciò è possibile per la navigazione "catene" che sono tutti i riferimenti o quando terminano con una singola raccolta.

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

Inclusione filtrata

Quando si applica Includi per caricare i dati correlati, è possibile aggiungere determinate operazioni enumerabili al riquadro di spostamento della raccolta incluso, che consente di filtrare e ordinare i risultati.

Le operazioni supportate sono: Where, OrderByDescendingOrderBy, ThenBy, ThenByDescending, Skip, e Take.

Tali operazioni devono essere applicate alla navigazione nella raccolta nell'espressione lambda passata al metodo Include, come illustrato nell'esempio seguente:

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();
}

Ogni navigazione inclusa consente un solo set univoco di operazioni di filtro. Nei casi in cui più operazioni di inclusione vengono applicate per una determinata navigazione nella raccolta (blog.Posts negli esempi seguenti), è possibile specificare le operazioni di filtro solo su una di esse:

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();
}

È invece possibile applicare operazioni identiche per ogni navigazione inclusa più volte:

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();
}

Attenzione

In caso di rilevamento delle query, i risultati dell'inclusione filtrata potrebbero essere imprevisti a causa della correzione dello spostamento. Tutte le entità rilevanti sottoposte a query per in precedenza e archiviate in Rilevamento modifiche saranno presenti nei risultati della query Include filtrata, anche se non soddisfano i requisiti del filtro. È consigliabile usare NoTracking query o ricreare DbContext quando si usa l'inclusione filtrata in tali situazioni.

Esempio:

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();

Nota

In caso di query di rilevamento, la navigazione in base alla quale è stata applicata l'inclusione filtrata viene considerata caricata. Ciò significa che EF Core non tenterà di ricaricare i valori usando il caricamento esplicito o il caricamento differita, anche se alcuni elementi potrebbero essere ancora mancanti.

Inclusione per i tipi derivati

È possibile includere i dati correlati dalla struttura di spostamento definita solo in un tipo derivato usando Include e ThenInclude.

Dato il modello seguente:

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; }
}

Il contenuto della navigazione di School tutte le Persone che sono Studenti può essere caricato con entusiasmo usando molti modelli:

  • Uso del cast

    context.People.Include(person => ((Student)person).School).ToList()
    
  • Uso dell'operatore as

    context.People.Include(person => (person as Student).School).ToList()
    
  • Uso dell'overload di Include che accetta il parametro di tipo string

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

Configurazione del modello per gli spostamenti automatici inclusi

È possibile configurare una navigazione nel modello da includere ogni volta che l'entità viene caricata dal database usando AutoInclude il metodo . Ha lo stesso effetto di specificare Include con lo spostamento in ogni query in cui viene restituito il tipo di entità nei risultati. Nell'esempio seguente viene illustrato come configurare una navigazione da includere automaticamente.

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

Dopo la configurazione precedente, l'esecuzione di una query simile alla seguente caricherà ColorScheme lo spostamento per tutti i temi nei risultati.

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

Questa configurazione viene applicata a ogni entità restituita nel risultato indipendentemente dalla modalità di visualizzazione nei risultati. Ciò significa che se un'entità è nel risultato a causa dell'uso di uno spostamento, usando Include un altro tipo di entità o la configurazione di inclusione automatica, caricherà tutti gli spostamenti inclusi automaticamente per esso. La stessa regola si estende agli spostamenti configurati come inclusi automaticamente nel tipo derivato dell'entità.

Se per una determinata query non si vogliono caricare i dati correlati tramite una struttura di spostamento, configurata a livello di modello per l'inserimento automatico, è possibile usare IgnoreAutoIncludes il metodo nella query. L'uso di questo metodo interromperà il caricamento di tutti gli spostamenti configurati come inclusione automatica da parte dell'utente. L'esecuzione di una query come di seguito restituirà tutti i temi dal database, ma non verrà caricata ColorScheme anche se è configurata come navigazione inclusa automaticamente.

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

Nota

Anche gli spostamenti ai tipi di proprietà vengono configurati come inclusi automaticamente per convenzione e l'uso IgnoreAutoIncludes dell'API non impedisce l'inserimento. Verranno comunque inclusi nei risultati della query.