Carga de entidades relacionadas

Entity Framework admite tres maneras de cargar datos relacionados: carga diligente, carga diferida y carga explícita. Las técnicas que se muestran en este tema se aplican igualmente a los modelos creados con Code First y EF Designer.

Carga diligente

La carga diligente es el proceso por el que una consulta de un tipo de entidad también carga entidades relacionadas como parte de la consulta. La carga diligente se logra mediante el uso del método Include. Por ejemplo, las consultas siguientes cargarán blogs y todas las entradas relacionadas con cada 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 es un método de extensión en el espacio de nombres System.Data.Entity, por lo que es necesario asegurarse de que se use ese espacio de nombres.

Carga diligente de varios niveles

También es posible cargar diligentemente varios niveles de entidades relacionadas. Las consultas siguientes muestran ejemplos de cómo hacerlo para las propiedades de navegación de colección y referencia.

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:

Actualmente no es posible filtrar qué entidades relacionadas se cargan. Include siempre incluirá todas las entidades relacionadas.

Carga diferida

La carga diferida es el proceso por el que se carga automáticamente una entidad o colección de entidades desde la base de datos la primera vez que se acceda a una propiedad que haga referencia a la entidad o a las entidades. Al usar tipos de entidad POCO, la carga diferida se logra mediante la creación de instancias de tipos de proxy derivados y, a continuación, el reemplazo de las propiedades virtuales para la adición del enlace de carga. Por ejemplo, al usar la clase de entidad Blog definida a continuación, las entradas relacionadas se cargarán la primera vez que se obtenga acceso a la propiedad de navegación Publicaciones:

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

Desactivar la carga diferida para la serialización

La carga diferida y la serialización no combinan bien y, si no se tiene cuidado, se podría terminar consultando toda la base de datos solo por tener la carga diferida habilitada. La mayoría de los serializadores funcionan accediendo a cada propiedad en una instancia de un tipo. El acceso a propiedades desencadena la carga diferida, por lo que se serializan más entidades. Se accede a propiedades en esas entidades e, incluso, se cargan más entidades. Se recomienda desactivar la carga diferida antes de serializar una entidad. Se muestra cómo hacerlo en las secciones siguientes.

Desactivación de la carga diferida para propiedades de navegación específicas

La carga diferida de la colección Posts se puede desactivar haciendo que la propiedad Posts no sea virtual:

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

La carga de la colección Posts todavía se puede lograr mediante la carga diligente (consulte Carga diligente más arriba) o el método Load (consulte Carga explícita a continuación).

Desactivar la carga diferida para todas las entidades

La carga diferida se puede desactivar para todas las entidades del contexto estableciendo una marca en la propiedad Configuration. Por ejemplo:

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

La carga de entidades relacionadas todavía se puede lograr mediante la carga diligente (consulte Carga diligente más arriba) o el método Load (consulte Carga explícita a continuación).

Carga explícita

Incluso con la carga diferida deshabilitada, todavía sería posible cargar entidades relacionadas de forma diferida, pero debería realizarse con una llamada explícita. Para ello, use el método Load en la entrada de la entidad relacionada. Por ejemplo:

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:

El método Reference se debe usar cuando una entidad tenga una propiedad de navegación a otra sola entidad. Por otro lado, el método Collection deberá usarse cuando una entidad tenga una propiedad de navegación a una colección de otras entidades.

El método Query proporciona acceso a la consulta subyacente que Entity Framework usará al cargar entidades relacionadas. A continuación, se podrá usar LINQ para aplicar filtros a la consulta antes de ejecutarlo con una llamada a un método de extensión LINQ, como ToList, Load, etc. El método Query se puede usar con las propiedades de navegación de referencia y colección, pero resulta más útil para las colecciones en las que solo se pueda usar para cargar parte de la colección. Por ejemplo:

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

Cuando se use el método Query, normalmente será mejor desactivar la carga diferida para la propiedad de navegación. Esto se debe a que, de lo contrario, toda la colección podría cargarse automáticamente con el mecanismo de carga diferida antes o después de que se haya ejecutado la consulta filtrada.

Nota:

Aunque se puede especificar la relación como una cadena en lugar de una expresión lambda, la devolución de IQueryable no es genérica cuando se use una cadena y, por lo tanto, el método Cast suele ser necesario antes de que se pueda hacer cualquier cosa útil con ella.

A veces resulta útil saber cuántas entidades están relacionadas con otra entidad de la base de datos sin incurrir realmente en el coste de cargar todas esas entidades. Se puede usar el método Query con el método de recuento LINQ para hacerlo. Por ejemplo:

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