Carregamento de dados relacionadosLoading Related Data

Entity Framework Core permite que você use as propriedades de navegação em seu modelo para carregar as entidades relacionadas.Entity Framework Core allows you to use the navigation properties in your model to load related entities. Há três padrões de S/RM comuns usados para carregar dados relacionados.There are three common O/RM patterns used to load related data.

  • Carregamento adiantado significa que os dados relacionados são carregados do banco de dados como parte da consulta inicial.Eager loading means that the related data is loaded from the database as part of the initial query.
  • Carregamento explícito significa que os dados relacionados são explicitamente carregados do banco de dados em um momento posterior.Explicit loading means that the related data is explicitly loaded from the database at a later time.
  • Carregamento preguiçoso significa que os dados relacionados são transparentemente carregados do banco de dados quando a propriedade de navegação é acessada.Lazy loading means that the related data is transparently loaded from the database when the navigation property is accessed.

Dica

Veja o exemplo deste artigo no GitHub.You can view this article's sample on GitHub.

Carregamento adiantadoEager loading

Você pode usar o método Include para especificar dados relacionados a serem incluídos nos resultados da consulta.You can use the Include method to specify related data to be included in query results. No exemplo a seguir, os blogs que são retornados nos resultados terão suas propriedades Posts preenchidas com as postagens relacionadas.In the following example, the blogs that are returned in the results will have their Posts property populated with the related posts.

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 foram previamente carregadas para a instância de contexto.Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. Dessa forma, mesmo se você não incluir explicitamente os dados para uma propriedade de navegação, a propriedade ainda poderá ser populada se algumas ou todas as entidades relacionadas foram carregadas anteriormente.So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

Você pode incluir dados relacionados de várias relações em uma única consulta.You can include related data from multiple relationships in a single query.

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

Incluindo vários níveisIncluding multiple levels

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.You can drill down thru relationships to include multiple levels of related data using the ThenInclude method. O exemplo a seguir carrega todos os blogs, suas postagens relacionadas e o autor de cada postagem.The following example loads all blogs, their related posts, and the author of each post.

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

Observação

As versões atuais do Visual Studio oferecem opções de conclusão de código incorreto, que podem causar expressões corretas a serem sinalizadas com erros de sintaxe ao usar o método ThenInclude após uma propriedade de navegação da coleção.Current versions of Visual Studio offer incorrect code completion options and can cause correct expressions to be flagged with syntax errors when using the ThenInclude method after a collection navigation property. Este é um sintoma de um bug de IntelliSense controlado no https://github.com/dotnet/roslyn/issues/8237.This is a symptom of an IntelliSense bug tracked at https://github.com/dotnet/roslyn/issues/8237. É seguro ignorar esses erros de sintaxe artificiais desde que o código esteja correto e possa ser compilado com êxito.It is safe to ignore these spurious syntax errors as long as the code is correct and can be compiled successfully.

É possível encadear chamadas múltiplas para ThenInclude para continuar incluindo mais níveis de dados relacionados.You can chain multiple calls to ThenInclude to continue including further levels of related data.

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 tudo isso para incluir dados relacionados de vários níveis e várias raízes na mesma consulta.You can combine all of this to include related data from multiple levels and multiple roots in the same query.

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.You may want to include multiple related entities for one of the entities that is being included. Por exemplo, ao consultar Blog, você inclui Posts e deseja incluir o Author e Tags dos Posts.For example, when querying Blogs, you include Posts and then want to include both the Author and Tags of the Posts. Para fazer isso, você precisa especificar cada Include a partir da raiz do caminho.To do this, you need to specify each include path starting at the root. Por exemplo, Blog -> Posts -> Author e Blog -> Posts -> Tags.For example, Blog -> Posts -> Author and Blog -> Posts -> Tags. Isso não significa que você obterá junções redundantes, na maioria dos casos o EF irá consolidar as associações durante a geração de SQL.This does not mean you will get redundant joins, in most cases EF will consolidate the joins when generating 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();
}

Incluir em tipos derivadosInclude on derived types

Você pode incluir dados relacionados de navegações definidos apenas em um tipo derivado usando Include e ThenInclude.You can include related data from navigations defined only on a derived type using Include and ThenInclude.

Recebe o seguinte modelo:Given the following model:

    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 de navegação de School de todas as pessoas que são alunos pode ser carregado ansiosamente usando um número de padrões:Contents of School navigation of all People who are Students can be eagerly loaded using a number of patterns:

  • usando castusing cast

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

    context.People.Include(person => (person as Student).School).ToList()
    
  • usando a sobrecarga de Include que usa o parâmetro de tipo stringusing overload of Include that takes parameter of type string

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

Operadores include ignoradosIgnored includes

Se você alterar a consulta para que ela não retorne instâncias do tipo de entidade com o qual ela começa, então os operadores include serão ignorados. If you change the query so that it no longer returns instances of the entity type that the query began with, then the include operators are ignored.

No exemplo a seguir, os operadores de inclusão se baseiam no Blog, mas em seguida o operador Select é usado para alterar a consulta para retornar um tipo anônimo.In the following example, the include operators are based on the Blog, but then the Select operator is used to change the query to return an anonymous type. Nesse caso, os operadores de inclusão não têm efeito.In this case, the include operators have no effect.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .Select(blog => new
        {
            Id = blog.BlogId,
            Url = blog.Url
        })
        .ToList();
}

Por padrão, o EF Core registrará um aviso quando operadores de inclusão são ignorados.By default, EF Core will log a warning when include operators are ignored. Consulte log para obter mais informações sobre como exibir a saída de log.See Logging for more information on viewing logging output. Você pode alterar o comportamento quando um operador include é ignorado para gerar ou não fazer nada.You can change the behavior when an include operator is ignored to either throw or do nothing. Isso é feito ao configurar as opções para o seu contexto - normalmente em DbContext.OnConfiguring, ou em Startup.cs se você estiver usando o ASP.NET Core.This is done when setting up the options for your context - typically in DbContext.OnConfiguring, or in Startup.cs if you are using ASP.NET Core.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;ConnectRetryCount=0")
        .ConfigureWarnings(warnings => warnings.Throw(CoreEventId.IncludeIgnoredWarning));
}

Carregamento explícitoExplicit loading

Observação

Esse recurso foi introduzido no EF Core 1.1.This feature was introduced in EF Core 1.1.

Você pode carregar explicitamente uma propriedade de navegação por meio da API DbContext.Entry(...).You can explicitly load a navigation property via the DbContext.Entry(...) API.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    context.Entry(blog)
        .Collection(b => b.Posts)
        .Load();

    context.Entry(blog)
        .Reference(b => b.Owner)
        .Load();
}

Você pode carregar também explicitamente uma propriedade de navegação executando uma consulta separada que retorna as entidades relacionadas.You can also explicitly load a navigation property by executing a seperate query that returns the related entities. Se o controle de alterações estiver habilitado, ao carregar uma entidade, o EF Core vai automaticamente definir as propriedades de navegação da entidade carregada recentemente para se referir a qualquer entidade já carregada e definir as propriedades de navegação das entidades já carregadas para se referir às entidades carregadas recentemente.If change tracking is enabled, then when loading an entity, EF Core will automatically set the navigation properties of the newly-loaded entitiy to refer to any entities already loaded, and set the navigation properties of the already-loaded entities to refer to the newly-loaded entity.

Você também pode obter uma consulta LINQ que representa o conteúdo de uma propriedade de navegação.You can also get a LINQ query that represents the contents of a navigation property.

Isso permite que você faça coisas como a execução de um operador de agregação sobre as entidades relacionadas sem carregá-los na memória.This allows you to do things such as running an aggregate operator over the related entities without loading them into memory.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    var postCount = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Count();
}

Você também pode filtrar quais entidades relacionadas são carregadas na memória.You can also filter which related entities are loaded into memory.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    var goodPosts = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Where(p => p.Rating > 3)
        .ToList();
}

Carregamento preguiçosoLazy loading

Observação

Esse recurso foi introduzido no EF Core 2.1.This feature was introduced in EF Core 2.1.

A maneira mais simples de usar o carregamento lento é instalando o pacote Microsoft.EntityFrameworkCore.Proxies e o habilitando com uma chamada para UseLazyLoadingProxies.The simplest way to use lazy-loading is by installing the Microsoft.EntityFrameworkCore.Proxies package and enabling it with a call to UseLazyLoadingProxies. Por exemplo:For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseLazyLoadingProxies()
        .UseSqlServer(myConnectionString);

Ou, ao usar AddDbContext:Or when using AddDbContext:

    .AddDbContext<BloggingContext>(
        b => b.UseLazyLoadingProxies()
              .UseSqlServer(myConnectionString));

Em seguida, EF Core habilitará o carregamento lento para qualquer propriedade de navegação que pode ser substituída – isto é, ela deve ser virtual e estar em uma classe da qual pode ser herdada.EF Core will then enable lazy-loading for any navigation property that can be overridden--that is, it must be virtual and on a class that can be inherited from. Por exemplo, nas entidades a seguir, as propriedades de navegação Post.Blog e Blog.Posts terão carregamento lento.For example, in the following entities, the Post.Blog and Blog.Posts navigation properties will be lazy-loaded.

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public virtual Blog Blog { get; set; }
}

Carregamento lento sem proxiesLazy-loading without proxies

Proxies de carregamento lento trabalham inserindo o serviço ILazyLoader em uma entidade, conforme descrito em construtores de tipo de entidade.Lazy-loading proxies work by injecting the ILazyLoader service into an entity, as described in Entity Type Constructors. Por exemplo:For example:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts
    {
        get => LazyLoader?.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader?.Load(this, ref _blog);
        set => _blog = value;
    }
}

sso não requer que os tipos de entidade sejam herdados de ou que as propriedades de navegação sejam virtuais e permite que que instâncias da entidade criadas com new carreguem lentamente quando conectadas a um contexto.This doesn't require entity types to be inherited from or navigation properties to be virtual and allows entity instances created with new to lazy-load once attached to a context. No entanto, ele requer uma referência para o serviço ILazyLoader, que associa os tipos de entidade para o assembly principal EF.However, it requires a reference to the ILazyLoader service, which couples entity types to the EF Core assembly. Para evitar isso, o núcleo EF permite que o método ILazyLoader.Load seja inserido como um representante. To avoid this EF Core allows the ILazyLoader.Load method to be injected as a delegate. Por exemplo:For example:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

    public int Id { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts
    {
        get => LazyLoader?.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader?.Load(this, ref _blog);
        set => _blog = value;
    }
}

O código acima usa um método de extensão Load para fazer o uso do delegate um pouco mais limpo:The code above uses a Load extension method to make using the delegate a bit cleaner:

public static class PocoLoadingExtensions
{
    public static TRelated Load<TRelated>(
        this Action<object, string> loader,
        object entity,
        ref TRelated navigationField,
        [CallerMemberName] string navigationName = null)
        where TRelated : class
    {
        loader?.Invoke(entity, navigationName);

        return navigationField;
    }
}

Observação

O parâmetro de construtor para o representante de carregamento lento deve ser chamado "lazyLoader".The constructor parameter for the lazy-loading delegate must be called "lazyLoader". Configuração para usar um nome diferente, que isso é planejado para uma versão futura.Configuration to use a different name this is planned for a future release.

Como o EF Core irá automaticamente corrigir as propriedades de navegação, você pode acabar com ciclos em seu gráfico de objetos.Because EF Core will automatically fix-up navigation properties, you can end up with cycles in your object graph. Por exemplo, o carregamento de um blog suas postagens relacionadas resultará em um objeto de blog referenciando uma coleção de postagens.For example, Loading a blog and it's related posts will result in a blog object that references a collection of posts. Cada uma dessas postagens terá uma referência de volta para o blog.Each of those posts will have a reference back to the blog.

Algumas estruturas de serialização não permitem esses ciclos.Some serialization frameworks do not allow such cycles. Por exemplo, Json.NET lançará a exceção a seguir se for encontrado um ciclo.For example, Json.NET will throw the following exception if a cycle is encountered.

Newtonsoft.Json.JsonSerializationException: Self referenciando loop detectado para a propriedade 'Blog' com tipo 'MyApplication.Models.Blog'.Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Blog' with type 'MyApplication.Models.Blog'.

Se você estiver usando o ASP.NET Core, poderá configurar Json.NET para ignorar ciclos que ele encontrar no gráfico de objeto.If you are using ASP.NET Core, you can configure Json.NET to ignore cycles that it finds in the object graph. Isso é feito no método ConfigureServices(...) em Startup.cs.This is done in the ConfigureServices(...) method in Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddMvc()
        .AddJsonOptions(
            options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
        );

    ...
}