Caricamento di entità correlate

Entity Framework supporta tre modalità di caricamento dei dati correlati: caricamento eager, lazy loading e caricamento esplicito. Le tecniche illustrate in questo argomento si applicano allo stesso modo ai modelli creati con Code First e Ef Designer.

Caricamento immediato

Il caricamento ansioso è il processo in cui una query per un tipo di entità carica anche le entità correlate come parte della stessa query. Il caricamento eager viene realizzato usando il metodo Include. Ad esempio, le query seguenti caricheranno i blog e tutti i post correlati a ogni blog.

using (var context = new BloggingContext())
{
    // Load all blogs and related posts.
    var blogs1 = context.Blogs
                        .Include(b => b.Posts)
                        .ToList();

    // Load one blog and its related posts.
    var blog1 = context.Blogs
                       .Where(b => b.Name == "ADO.NET Blog")
                       .Include(b => b.Posts)
                       .FirstOrDefault();

    // Load all blogs and related posts
    // using a string to specify the relationship.
    var blogs2 = context.Blogs
                        .Include("Posts")
                        .ToList();

    // Load one blog and its related posts
    // using a string to specify the relationship.
    var blog2 = context.Blogs
                       .Where(b => b.Name == "ADO.NET Blog")
                       .Include("Posts")
                       .FirstOrDefault();
}

Annotazioni

Include è un metodo di estensione nello spazio dei nomi System.Data.Entity, quindi assicurarsi di usare tale spazio dei nomi.

Caricamento di più livelli con entusiasmo

È anche possibile caricare con entusiasmo più livelli di entità correlate. Le query seguenti mostrano esempi di come eseguire questa operazione sia per le proprietà di raccolta che per le proprietà di navigazione di riferimento.

using (var context = new BloggingContext())
{
    // Load all blogs, all related posts, and all related comments.
    var blogs1 = context.Blogs
                        .Include(b => b.Posts.Select(p => p.Comments))
                        .ToList();

    // Load all users, their related profiles, and related avatar.
    var users1 = context.Users
                        .Include(u => u.Profile.Avatar)
                        .ToList();

    // Load all blogs, all related posts, and all related comments  
    // using a string to specify the relationships.
    var blogs2 = context.Blogs
                        .Include("Posts.Comments")
                        .ToList();

    // Load all users, their related profiles, and related avatar  
    // using a string to specify the relationships.
    var users2 = context.Users
                        .Include("Profile.Avatar")
                        .ToList();
}

Annotazioni

Non è attualmente possibile filtrare le entità correlate caricate. L'inclusione porterà sempre tutte le entità correlate.

Caricamento pigro

Il caricamento differito è il processo in cui un'entità o una raccolta di entità viene automaticamente caricata dal database la prima volta che si accede a una proprietà che fa riferimento all'entità o alle entità. Quando si usano tipi di entità POCO, il caricamento differito viene ottenuto creando istanze di tipi proxy derivati e quindi sovrascrivendo le proprietà virtuali per aggiungere l'hook di caricamento. Ad esempio, quando si usa la classe di entità Blog definita di seguito, i post correlati verranno caricati la prima volta che si accede alla proprietà di navigazione Post:

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public string Tags { get; set; }

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

Disattivare il caricamento ritardato per la serializzazione

Il caricamento differito e la serializzazione non si combinano correttamente e, se non si è attenti, potresti finire per interrogare l'intero database solo perché il caricamento differito è abilitato. La maggior parte dei serializzatori funziona accedendo a ciascuna proprietà in un'istanza di un tipo. L'accesso alle proprietà attiva il caricamento differito, quindi più entità vengono serializzate. In tali entità si accede alle proprietà e vengono caricate ancora più entità. È consigliabile disattivare il caricamento differito prima di serializzare un'entità. Le sezioni seguenti illustrano come eseguire questa operazione.

Disattivazione del caricamento differito per proprietà di navigazione specifiche

Il caricamento differito della raccolta Posts può essere disabilitato rendendo la proprietà Posts non virtuale.

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public string Tags { get; set; }

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

Il caricamento della raccolta Post può comunque essere ottenuto usando il caricamento anticipato (vedere Caricamento con Anticipo sopra) o il metodo Load (vedere Caricamento esplicito di seguito).

Disattivare il caricamento differito per tutte le entità

Il caricamento differito può essere disattivato per tutte le entità nel contesto impostando un flag sulla proprietà di configurazione. Per esempio:

public class BloggingContext : DbContext
{
    public BloggingContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}

Il caricamento delle entità correlate può comunque essere ottenuto usando il caricamento eager (vedere Eagerly Loading above) o il metodo Load (vedere Caricamento esplicito di seguito).

Caricamento esplicito

Anche se il caricamento differito è disabilitato, è comunque possibile caricare in modo differito le entità correlate, ma deve essere eseguito con una chiamata esplicita. A tale scopo, si utilizza il metodo Load nella voce dell'entità correlata. Per esempio:

using (var context = new BloggingContext())
{
    var post = context.Posts.Find(2);

    // Load the blog related to a given post.
    context.Entry(post).Reference(p => p.Blog).Load();

    // Load the blog related to a given post using a string.
    context.Entry(post).Reference("Blog").Load();

    var blog = context.Blogs.Find(1);

    // Load the posts related to a given blog.
    context.Entry(blog).Collection(p => p.Posts).Load();

    // Load the posts related to a given blog
    // using a string to specify the relationship.
    context.Entry(blog).Collection("Posts").Load();
}

Annotazioni

Il metodo Reference deve essere usato quando un'entità dispone di una proprietà di navigazione a un'altra singola entità. D'altra parte, il metodo Collection dovrebbe essere usato quando un'entità ha una proprietà di navigazione verso una raccolta di altre entità.

Il metodo Query fornisce l'accesso alla query sottostante che Entity Framework userà durante il caricamento di entità correlate. È quindi possibile usare LINQ per applicare filtri alla query prima di eseguirlo con una chiamata a un metodo di estensione LINQ, ad esempio ToList, Load e così via. Il metodo Query può essere usato con proprietà di navigazione di riferimento e raccolta, ma è più utile per le raccolte in cui può essere usato per caricare solo parte della raccolta. Per esempio:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Load the posts with the 'entity-framework' tag related to a given blog.
    context.Entry(blog)
           .Collection(b => b.Posts)
           .Query()
           .Where(p => p.Tags.Contains("entity-framework"))
           .Load();

    // Load the posts with the 'entity-framework' tag related to a given blog
    // using a string to specify the relationship.
    context.Entry(blog)
           .Collection("Posts")
           .Query()
           .Where(p => p.Tags.Contains("entity-framework"))
           .Load();
}

Quando si utilizza il metodo Query, di solito è meglio disattivare il caricamento differito per la proprietà di navigazione. Ciò è dovuto al fatto che in caso contrario l'intera raccolta può essere caricata automaticamente dal meccanismo di caricamento differito prima o dopo l'esecuzione della query filtrata.

Annotazioni

Sebbene la relazione possa essere specificata come stringa invece di un'espressione lambda, l'oggetto IQueryable restituito non è generico quando viene usata una stringa e pertanto il metodo Cast è in genere necessario prima di poter eseguire qualsiasi operazione utile.

A volte è utile sapere quante entità sono correlate a un'altra entità nel database senza incorrere effettivamente nel costo del caricamento di tutte le entità. A tale scopo, è possibile usare il metodo Query con il metodo LINQ Count. Per esempio:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    // Count how many posts the blog has.
    var postCount = context.Entry(blog)
                           .Collection(b => b.Posts)
                           .Query()
                           .Count();
}