Carregamento ansioso 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

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

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 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 na raiz. Por exemplo, Blog -> Posts -> Author e Blog -> Posts -> Tags. Isso não significa que você terá 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çãos usando um único Include método. Isso é possível para "cadeias" de navegação que são todas referências ou quando 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

Observação

Esse recurso foi introduzido no EF Core 5.0.

Ao aplicar Include a dados relacionados ao carregamento, 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, e SkipTake.

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. 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 = 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 de Filtered Include podem ser inesperados devido à correção de navegação. Todas as entidades relevantes que foram consultadas anteriormente e que 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 NoTracking consultas ou recriar 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 consultas de acompanhamento, a navegação na qual a inclusão filtrada foi aplicada é 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 para tipos derivados

Você pode incluir dados relacionados da navegação definidos apenas em um tipo derivado usando 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 de School todas as pessoas que são estudantes pode ser carregado com muita vontade usando muitos padrões:

  • Usando conversão

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

    context.People.Include(person => (person as Student).School).ToList()
    
  • Usar a sobrecarga disso Include usa o parâmetro de tipo string

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

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

Observação

Esse recurso foi introduzido no EF Core 6.0.

Você pode configurar uma navegação no modelo a ser incluída sempre que a entidade for carregada do banco de dados usando AutoInclude o método. Ele 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 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á ColorScheme a navegação para todos os temas nos resultados.

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

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, usando Include outro tipo de entidade ou configuração de inclusão automática, ela carregará todas as navegaçãos incluídas automaticamente para ela. A mesma regra se estende às navegaçãos configuradas como incluídas automaticamente no tipo derivado da entidade.

Se para uma consulta específica você não quiser carregar os dados relacionados por meio de uma navegação, configurada no nível do modelo para ser incluída automaticamente, você poderá 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. Executar uma consulta como abaixo trará de volta todos os temas do banco de dados, mas não será carregado ColorScheme mesmo que esteja configurado como navegação incluída automaticamente.

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

Observação

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