Carregamento adiantado de dados relacionados

Carregamento adiantado

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

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

Dica

O Entity Framework Core corrigirá automaticamente as propriedades de navegação para outras entidades que forem carregadas anteriormente na instância do contexto. Então, mesmo se você não incluir de forma explícita os dados para a propriedade de navegação, a propriedade ainda pode ser populada se algumas ou todas as entidades relacionadas foram carregadas anteriormente.

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

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

Cuidado

O carregamento adiantado de uma navegação de coleção em uma única consulta pode causar problemas de desempenho. Para obter mais informações, consulte Consultas individuais vs. consultas divididas.

Incluindo vários níveis

Você pode fazer uma busca detalhada por meio de 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 = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ToList();
}

É possível encadear chamadas múltiplas a ThenInclude para continuar incluindo outros níveis de dados relacionados.

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

Você pode combinar todas as chamadas para incluir os dados relacionados de vários níveis e várias raízes na mesma consulta.

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

Você talvez queira incluir várias entidades relacionadas para uma das entidades que está sendo incluída. Por exemplo, ao consultar os Blogs, você inclui os Posts e, em seguida, o Author e as Tags dos Posts. Para incluir ambos, você precisa especificar cada caminho de inclusão começando pela 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 = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags)
        .ToList();
}

Dica

Você também pode carregar várias navegações utilizando um único método Include. Isso é possível para "cadeias" de navegação que sejam todas referências ou quando elas terminam com uma única coleção.

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

Inclusão Filtrada

Ao aplicar o Include para carregar os dados relacionados, você pode adicionar determinadas operações enumeráveis à navegação da coleção incluída, o que permite a filtragem e a classificação dos resultados.

As operações que dão suporte são: Where, OrderBy, OrderByDescending, ThenBy, ThenByDescending, Skip e 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 = context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToList();
}

Cada navegação incluída permite apenas um conjunto exclusivo de operações de filtro. Em casos em que várias operações Include 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 = 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))
        .ToList();
}

Em vez disso, operações idênticas podem ser aplicadas a cada navegação incluída várias vezes:

using (var context = new BloggingContext())
{
    var filteredBlogs = 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))
        .ToList();
}

Cuidado

No caso de acompanhamento de consultas, os resultados da Inclusão Filtrada podem ser inesperados devido a correção da navegação. Todas as entidades relevantes consultadas anteriormente e armazenadas no Controlador de Alterações estarão presentes nos resultados da consulta de Inclusão Filtrada, mesmo que não atendam aos requisitos do filtro. Considere a possibilidade de usar consultas NoTracking ou de criar novamente o DbContext ao usar a Inclusão Filtrada nessas situações.

Exemplo:

var orders = context.Orders.Where(o => o.Id > 1000).ToList();

// customer entities will have references to all orders where Id > 1000, rather than > 5000
var filtered = context.Customers.Include(c => c.Orders.Where(o => o.Id > 5000)).ToList();

Observação

No caso de acompanhamento de consultas, a navegação na qual a Inclusão Filtrada foi aplicada é considerada como carregada. Isso significa que o Entity Framework Core não tentará recarregar seus valores utilizando o carregamento explícito ou o carregamento lento, mesmo que alguns elementos ainda possam estar ausentes.

Incluir para tipos derivados

Você pode incluir os dados relacionados da navegação definida somente em um tipo derivado utilizando Include e ThenInclude.

Com 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 School de todas as pessoas que são alunos pode ser carregado antecipadamente utilizando muitos padrões:

  • Usando a conversão

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

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

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

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

Você pode configurar uma navegação no modelo para ser incluída toda vez que a entidade for carregada do banco de dados utilizando o método AutoInclude. Isso tem o mesmo efeito que especificar Include com a navegação em cada consulta em que o tipo de entidade é retornado nos resultados. O exemplo a seguir mostra como fazer para configurar uma navegação para 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 = context.Themes.ToList();
}

Essa configuração é aplicada a cada entidade retornada 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, utilizando Include sobre outro tipo de entidade ou 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 derivado da entidade.

Se, para uma determinada consulta, você não deseja carregar os dados relacionados por meio de uma navegação configurada no nível do modelo para ser incluída automaticamente, você pode utilizar o método IgnoreAutoIncludes na sua consulta. O uso desse método interromperá o carregamento de todas as navegações configuradas como inclusão automática pelo usuário. A execução de uma consulta como a abaixo trará de volta todos os temas do banco de dados, mas não carregará ColorScheme, embora ele esteja configurado como navegação de inclusão automática.

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

Observação

As navegações para tipos proprietários também são configuradas como incluídas automaticamente por convenção e o uso da API IgnoreAutoIncludes não impede que elas sejam incluídas. Elas ainda serão incluídas nos resultados da consulta.