Compartilhar via


Carregamento antecipado de dados relacionados

Carregamento antecipado

Você pode usar o Include método para especificar os dados relacionados a serem incluídos nos resultados da consulta. No exemplo a seguir, os blogs retornados nos resultados terão sua Posts propriedade preenchida com as postagens relacionadas.

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

Dica

O Entity Framework Core corrigirá automaticamente as propriedades de navegação para quaisquer outras entidades que foram carregadas anteriormente na instância de contexto. Portanto, mesmo que você não inclua explicitamente os dados de uma propriedade de navegação, a propriedade ainda pode ser preenchida se algumas ou todas as entidades relacionadas tiverem sido carregadas anteriormente.

Você pode incluir dados relacionados de várias relações em uma única consulta.

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

Cuidado

Carregar uma navegação de coleção em uma única consulta pode causar problemas de desempenho. Para obter mais informações, consulte consultas únicas vs. divididas.

Incluindo vários níveis

Você pode aprofundar nas relações para incluir vários níveis de dados relacionados, usando o método ThenInclude. O exemplo a seguir carrega todos os blogs, suas postagens relacionadas e o autor de cada postagem.

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

Você pode encadear várias chamadas para ThenInclude para continuar a incluir mais níveis de dados relacionados.

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

Você pode combinar todas as chamadas para incluir dados relacionados de vários níveis e várias raízes na mesma 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();
}

Talvez você queira incluir várias entidades relacionadas para uma das entidades que estão sendo incluídas. Por exemplo, ao consultar Blogs, você inclui Posts e, em seguida, deseja incluir tanto Author quanto Tags do Posts. Para incluir ambos, você precisa especificar cada caminho de inclusão começando na raiz. Por exemplo, Blog -> Posts -> Author e Blog -> Posts -> Tags. Isso não significa que você obterá junções redundantes; na maioria dos casos, o EF combinará as junções ao gerar o 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();
}

Dica

Você também pode carregar várias navegações usando um único Include método. Isso é possível para "correntes" de navegação que são compostas apenas por referências, ou quando se encerram em uma única coleção.

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

Inclusão filtrada

Ao aplicar Include a dados relacionados à carga, você pode adicionar determinadas operações enumeráveis à navegação de coleção incluída, o que permite filtrar e classificar os resultados.

As operações com suporte são: Where, , OrderBy, OrderByDescending, ThenBy, , ThenByDescending, Skipe Take.

Essas operações devem ser aplicadas na navegação da coleção no lambda passado para o método Include, conforme mostrado no exemplo abaixo:

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 navegação incluída permite apenas um conjunto exclusivo de operações de filtro. Nos casos em que várias operações de inclusão são aplicadas a uma determinada navegação de coleção (blog.Posts nos exemplos abaixo), as operações de filtro só podem ser especificadas em uma delas:

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, operações idênticas podem ser aplicadas a cada navegação incluída várias vezes:

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

Cuidado

No caso de consultas de acompanhamento, os resultados de Filtered Include podem ser inesperados devido à correção de navegação. Todas as entidades relevantes que foram consultadas anteriormente e foram armazenadas no Change Tracker estarão presentes nos resultados da consulta Filtered Include, mesmo que não atendam aos requisitos do filtro. Considere usar consultas com NoTracking ou recriar o DbContext ao utilizar a Inclusão Filtrada nessas situações.

Exemplo:

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

Observação

No caso de rastreamento de consultas, a navegação na qual foi aplicada uma inclusão filtrada é considerada carregada. Isso significa que o EF Core não tentará recarregar seus valores usando carregamento explícito ou carregamento lento, mesmo que alguns elementos ainda possam estar ausentes.

Incluir em tipos derivados

Você pode incluir dados relacionados da navegação definidos apenas em um tipo derivado usando Include e ThenInclude.

Dado o seguinte 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; }
}

O conteúdo da navegação de School todas as pessoas que são estudantes pode ser carregado com muita vontade usando muitos padrões:

  • Usando casting

    context.People.Include(person => ((Student)person).School).ToList()
    
  • Usando o operador as

    context.People.Include(person => (person as Student).School).ToList()
    
  • Usar a sobrecarga de Include que aceita um parâmetro do tipo string

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

Configuração de modelo para inclusões automáticas de navegações

Você pode configurar uma navegação no modelo usando o método AutoInclude para ser incluída sempre que a entidade for carregada do banco de dados. Tem o mesmo efeito de especificar Include com a navegação em todas as consultas nas quais o tipo de entidade é retornado nos resultados. O exemplo a seguir mostra como configurar uma navegação a ser incluída automaticamente.

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

Após a configuração acima, a execução de uma consulta como a abaixo carregará a navegação ColorScheme para todos os temas nos resultados.

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

Essa configuração é aplicada em todas as entidades retornadas no resultado, independentemente de como ela apareceu nos resultados. Isso significa que, se uma entidade estiver no resultado devido ao uso de uma navegação, por estar acima de outro tipo de entidade ou por configuração de inclusão automática, ela carregará todas as navegações incluídas automaticamente para ela. A mesma regra se estende às navegações configuradas como incluídas automaticamente no tipo de entidade derivada.

Se para uma consulta específica você não quiser carregar os dados relacionados por meio de uma navegação, que está configurada no nível do modelo para ser incluída automaticamente, você pode usar IgnoreAutoIncludes o método em sua consulta. O uso desse método interromperá o carregamento de todas as navegaçãos configuradas como inclusão automática pelo usuário. A execução de uma consulta como a abaixo retornará todos os temas do banco de dados, mas ColorScheme não será carregado mesmo que esteja configurado como navegação incluída automaticamente.

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

Observação

As navegações para tipos próprios também são configuradas como incluídas automaticamente por convenção e o uso da API IgnoreAutoIncludes não as impede de serem incluídas. Eles ainda serão incluídos nos resultados da consulta.