Compartir por


Carga diligente de datos relacionados

Carga diligente

Puede usar el Include método para especificar los datos relacionados que se incluirán en los resultados de la consulta. En el ejemplo siguiente, los blogs que se devuelven en los resultados tendrán su Posts propiedad rellenada con las publicaciones relacionadas.

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

Sugerencia

Entity Framework Core corregirá automáticamente las propiedades de navegación de otras entidades que se habían cargado previamente en la instancia de contexto. Por lo tanto, aunque no incluya explícitamente los datos de una propiedad de navegación, es posible que la propiedad se complete si alguna o todas las entidades relacionadas se cargaron anteriormente.

Puede incluir datos relacionados de varias relaciones en una sola consulta.

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

Precaución

La carga ansiosa de la navegación de una colección en una sola consulta puede provocar problemas de rendimiento. Para obtener más información, consulte Consultas únicas frente a consultas divididas.

Inclusión de varios niveles

Puede explorar en profundidad las relaciones para incluir varios niveles de datos relacionados mediante el ThenInclude método . En el ejemplo siguiente se cargan todos los blogs, sus publicaciones relacionadas y el autor de cada publicación.

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

Puede encadenar varias llamadas a ThenInclude para seguir incluyendo más niveles de datos relacionados.

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

Puede combinar todas las llamadas para incluir datos relacionados de varios niveles y varias raíces en la misma consulta.

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

Es posible que quiera incluir varias entidades relacionadas para una de las entidades que se están incluyendo. Por ejemplo, al consultar Blogs, se incluye Posts, y luego se desea incluir tanto el Author como el Tags del Posts. Para incluir ambos, debe especificar cada ruta de inclusión desde la raíz. Por ejemplo, Blog -> Posts -> Author y Blog -> Posts -> Tags. No significa que obtendrá combinaciones redundantes; en la mayoría de los casos, EF combinará las combinaciones al generar 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();
}

Sugerencia

También puede cargar varias navegaciones mediante un único Include método. Esto es posible para cadenas de navegación que están compuestas completamente por referencias, o cuando terminan con una sola colección.

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

Inclusión filtrada

Al aplicar Include para cargar datos relacionados, puede agregar determinadas operaciones enumerables a la navegación de recopilación incluida, lo que permite filtrar y ordenar los resultados.

Las operaciones admitidas son: Where, OrderBy, OrderByDescending, ThenBy, ThenByDescending, y SkipTake.

Estas operaciones deben aplicarse en la navegación de colección en la expresión lambda pasada al método Include, como se muestra en el ejemplo siguiente.

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

Cada navegación incluida solo permite un conjunto único de operaciones de filtro. En los casos en los que se aplican varias operaciones include para una navegación de recopilación determinada (blog.Posts en los ejemplos siguientes), las operaciones de filtro solo se pueden especificar en una de ellas:

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

Como alternativa, se pueden aplicar operaciones idénticas para cada navegación que se incluye varias veces:

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

Precaución

En el caso de las consultas de seguimiento, los resultados de la inclusión filtrada pueden ser inesperados debido al ajuste de navegación. Todas las entidades pertinentes que se han consultado anteriormente y que se han almacenado en el Seguimiento de cambios estarán presentes en los resultados de la consulta Include filtrada, incluso si no cumplen los requisitos del filtro. Considere la posibilidad de usar NoTracking consultas o volver a crear el DbContext al usar la inclusión filtrada en esas situaciones.

Ejemplo:

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

Nota:

En el caso de las consultas de seguimiento, se considera que la navegación en la que se aplicó la inclusión filtrada está cargada. Esto significa que EF Core no intentará volver a cargar sus valores mediante la carga explícita o la carga diferida, aunque todavía podrían faltar algunos elementos.

Inclusión en tipos derivados

Puede incluir datos relacionados desde la navegación definida solo en un tipo derivado mediante Include y ThenInclude.

Dado el siguiente modelo:

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

Los contenidos de School la navegación de todos los estudiantes se pueden cargar rápidamente usando varios patrones.

  • Uso de casting

    context.People.Include(person => ((Student)person).School).ToList()
    
  • Uso del as operador

    context.People.Include(person => (person as Student).School).ToList()
    
  • Uso de la sobrecarga de Include que recibe un parámetro de tipo string

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

Configuración del modelo para la inclusión automática de las navegaciones

Puede configurar una navegación en el modelo para que se incluya cada vez que la entidad se cargue desde la base de datos usando el método AutoInclude. Tiene el mismo efecto que especificar Include al navegar en cada consulta donde se devuelve el tipo de entidad en los resultados. En el ejemplo siguiente se muestra cómo configurar una navegación para que se incluya automáticamente.

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

Después de la configuración anterior, ejecutar una consulta como la siguiente cargará la navegación ColorScheme para todos los temas de los resultados.

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

Esta configuración se aplica en cada entidad devuelta en el resultado, independientemente de cómo aparezca en los resultados. Esto significa que si una entidad está en el resultado debido al uso de una navegación, utilizando Include en lugar de otro tipo de entidad o la configuración de inclusión automática, cargará todas las navegaciones incluidas automáticamente para ella. La misma regla se extiende a las navegaciones configuradas como incluidas automáticamente en el tipo derivado de la entidad.

Si para una consulta determinada no desea cargar los datos relacionados a través de una navegación, que se configura en el nivel de modelo para incluirse automáticamente, puede usar el IgnoreAutoIncludes método en la consulta. El uso de este método dejará de cargar todas las navegaciones configuradas como autoincluidas por el usuario. La ejecución de una consulta como la siguiente devolverá todos los temas de la base de datos, pero no se cargará ColorScheme aunque esté configurado como navegación incluida automáticamente.

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

Nota:

Las navegaciones a tipos propietarios también se configuran como incluidas automáticamente por convención y el uso de la API IgnoreAutoIncludes no impide que se incluyan. Todavía se incluirán en los resultados de la consulta.