Caricamento di entità correlate

Entity Framework supporta tre modi per caricare i dati correlati: caricamento eager, caricamento differita e caricamento esplicito. Le tecniche illustrate in questo argomento si applicano in modo analogo ai modelli creati con Code First ed EF Designer.

Caricamento eagerly

Il caricamento eager è il processo in cui una query per un tipo di entità carica anche le entità correlate come parte della query. Il caricamento eager viene ottenuto tramite 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();
}

Nota

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

Caricamento desideroso di più livelli

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

Nota

Non è attualmente possibile filtrare le entità correlate caricate. Include porterà sempre tutte le entità correlate.

Caricamento lazy

Il caricamento differita è il processo in cui un'entità o una raccolta di entità viene caricata automaticamente 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 differita viene ottenuto creando istanze di tipi proxy derivati e quindi eseguendo l'override delle 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 differita per la serializzazione

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

Disattivazione del caricamento differita per proprietà di navigazione specifiche

Il caricamento differita della raccolta Posts può essere disattivato 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 eager (vedere Eagerly Loading sopra) o il metodo Load (vedere Caricamento esplicito di seguito).

Disattivare il caricamento differita per tutte le entità

Il caricamento differita può essere disattivato per tutte le entità nel contesto impostando un flag nella proprietà Configuration. Ad 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 differita è disabilitato, è comunque possibile caricare in modo differita le entità correlate, ma deve essere eseguito con una chiamata esplicita. A tale scopo, usare il metodo Load nella voce dell'entità correlata. Ad 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();
}

Nota

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 deve essere usato quando un'entità dispone di una proprietà di navigazione a 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. Ad 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 usa il metodo Query, in genere è consigliabile disattivare il caricamento differita per la proprietà di navigazione. Ciò è dovuto al fatto che in caso contrario l'intera raccolta può essere caricata automaticamente dal meccanismo di caricamento differita prima o dopo l'esecuzione della query filtrata.

Nota

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