Condividi tramite


Precaricamento dei dati correlati

Caricamento anticipato

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

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

Suggerimento

Entity Framework Core correggerà automaticamente le proprietà di navigazione in tutte le altre entità caricate in precedenza nell'istanza di contesto. Pertanto, anche se non si includono in modo esplicito i dati per una proprietà di navigazione, 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 = await context.Blogs
        .Include(blog => blog.Posts)
        .Include(blog => blog.Owner)
        .ToListAsync();
}

Attenzione

Il caricamento eager di una collezione tramite 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 usando il ThenInclude metodo . Nell'esempio seguente vengono caricati tutti i blog, i post correlati e l'autore di ogni post.

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

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

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

È 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 = await context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
        .Include(blog => blog.Owner)
        .ThenInclude(owner => owner.Photo)
        .ToListAsync();
}

È possibile includere più entità correlate per una delle entità incluse. Ad esempio, quando si esegue una query su Blogs, si include Posts e si desidera includere sia Author che Tags del 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 = await context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags)
        .ToListAsync();
}

Suggerimento

È anche possibile caricare più navigazioni usando un singolo metodo Include. Ciò è possibile per le "catene di navigazione" che sono tutte referenze o quando terminano con una singola raccolta.

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

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, OrderByOrderByDescending, 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 = await context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToListAsync();
}

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 = await 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))
        .ToListAsync();
}

In alternativa, è possibile applicare operazioni identiche per ogni navigazione inclusa più volte:

using (var context = new BloggingContext())
{
    var filteredBlogs = await 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))
        .ToListAsync();
}

Attenzione

In caso di tracciamento delle query, i risultati dell'inclusione filtrata potrebbero essere imprevisti a causa della correzione della navigazione. Tutte le entità rilevanti sottoposte in precedenza a query e memorizzate in "Rilevamento modifiche" saranno presenti nei risultati della query Filtered Include, 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 = await context.Orders.Where(o => o.Id > 1000).ToListAsync();

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

Annotazioni

In caso di interrogazioni di tracciamento, la navigazione su cui è 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.

Includere nei tipi derivati

È possibile includere i dati correlati dalla navigazione definita soltanto su 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 School navigazione di tutte le persone che sono Studenti può essere caricato con entusiasmo usando molti modelli:

  • Utilizzo del cast

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

    context.People.Include(person => (person as Student).School).ToList()
    
  • Utilizzo dell'overload di Include che accetta un 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 la navigazione in ogni query in cui il tipo di entità è restituito 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 la navigazione per tutti i temi sui risultati.

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

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 una navigazione, utilizzando Include su un altro tipo di entità o la configurazione di inclusione automatica, caricherà tutte le navigazioni incluse automaticamente per essa. La stessa regola si estende agli spostamenti configurati come inclusi automaticamente nel tipo derivato dell'entità.

Se per una specifica query non si vogliono caricare i dati correlati tramite una navigazione, configurata a livello di modello per l'inclusione automatica, è possibile usare il metodo IgnoreAutoIncludes nella query. L'uso di questo metodo interromperà il caricamento di tutte le navigazioni configurate come inclusione automatica impostata dall'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 = await context.Themes.IgnoreAutoIncludes().ToListAsync();
}

Annotazioni

Navigazioni verso tipi di proprietà vengono configurate come incluse automaticamente per convenzione, e l'uso dell'API IgnoreAutoIncludes non impedisce l'inclusione. Verranno comunque inclusi nei risultati della query.