Partilhar via


Rastreamento vs. consultas No-Tracking

O comportamento de rastreamento determina se o Entity Framework Core mantém informações sobre uma instância de entidade no seu rastreador de alterações. Se uma entidade for controlada, todas as alterações detetadas na entidade serão gravadas na base de dados durante SaveChanges. EF Core também corrige as propriedades de navegação entre as entidades em um resultado de consulta de rastreamento e as entidades que estão no rastreador de mudanças.

Observação

Os tipos de entidade sem chave nunca são rastreados. Sempre que este artigo menciona tipos de entidade, refere-se a tipos de entidade que têm uma chave definida.

Sugestão

Você pode visualizar a amostra do deste artigo no GitHub.

Consultas de rastreamento

Por padrão, as consultas que retornam tipos de entidade são rastreadas. Uma consulta de rastreamento significa que todas as alterações nas instâncias da entidade são persistidas pelo SaveChanges. No exemplo a seguir, a alteração na classificação de blogs é detetada e persistida no banco de dados durante SaveChanges:

var blog = await context.Blogs.SingleOrDefaultAsync(b => b.BlogId == 1);
blog.Rating = 5;
await context.SaveChangesAsync();

Quando os resultados são retornados em uma consulta de controle, o EF Core verifica se a entidade já está no contexto. Se o EF Core encontrar uma entidade existente, a mesma instância será retornada, o que pode potencialmente usar menos memória e ser mais rápido do que uma consulta sem rastreamento. O EF Core não substitui os valores atuais e originais das propriedades da entidade na entrada pelos valores do banco de dados. Se a entidade não for encontrada no contexto, o EF Core criará uma nova instância de entidade e a anexará ao contexto. Os resultados da consulta não contêm nenhuma entidade que foi adicionada ao contexto, mas que ainda não foi salva na base de dados.

Consultas sem rastreamento

As consultas sem rastreamento são úteis quando os resultados são usados em um cenário de apenas leitura. Eles geralmente são mais rápidos de executar porque não há necessidade de configurar as informações de controle de alterações. Se as entidades recuperadas do banco de dados não precisarem ser atualizadas, uma consulta sem rastreamento deverá ser usada. Uma consulta individual pode ser definida como sem rastreamento. Uma consulta sem acompanhamento também fornece resultados com base no que está no banco de dados, desconsiderando quaisquer alterações locais ou entidades adicionadas.

var blogs = await context.Blogs
    .AsNoTracking()
    .ToListAsync();

O comportamento de rastreamento padrão pode ser alterado no nível da instância de contexto:

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = await context.Blogs.ToListAsync();

A próxima seção explica quando uma consulta sem rastreamento pode ser menos eficiente do que uma consulta de rastreamento.

Resolução de identidade

Como uma consulta de acompanhamento usa o rastreador de alterações, o EF Core faz a resolução de identidade em uma consulta de rastreamento. Ao materializar uma entidade, o EF Core retorna a mesma instância de entidade do rastreador de alterações se ela já estiver sendo rastreada. Se o resultado contiver a mesma entidade várias vezes, a mesma instância será retornada para cada ocorrência. Consultas sem rastreamento:

  • Não use o rastreador de alterações e não faça a resolução de identidade.
  • Retornar uma nova instância da entidade mesmo quando a mesma entidade estiver contida no resultado várias vezes.

Rastreamento e não rastreamento podem ser combinados na mesma consulta. Ou seja, você pode ter uma consulta sem rastreamento, que faz a resolução de identidade nos resultados. Assim como AsNoTracking o operador consultável, adicionamos outro operador AsNoTrackingWithIdentityResolution<TEntity>(IQueryable<TEntity>). Há também uma entrada associada adicionada no QueryTrackingBehavior enum. Quando a consulta para usar a resolução de identidade é configurada sem rastreamento, um rastreador de alterações autónomo é usado em segundo plano ao gerar resultados de consulta, de modo que cada instância seja criada apenas uma vez. Como esse rastreador de alterações é diferente daquele no contexto, os resultados não são rastreados pelo contexto. Depois que a consulta é enumerada completamente, o rastreador de alterações sai do escopo e o lixo é coletado conforme necessário.

var blogs = await context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .ToListAsync();

Configurando o comportamento de rastreamento padrão

Se estiveres a alterar o comportamento de rastreamento para muitas consultas, pode ser preferível alterar o padrão por defeito:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying.Tracking;Trusted_Connection=True;ConnectRetryCount=0")
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}

Isso faz com que todas as suas consultas não sejam rastreadas por padrão. Você ainda pode adicionar AsTracking para fazer o acompanhamento de consultas específicas.

Acompanhamento e projeções personalizadas

Mesmo que o tipo de resultado da consulta não seja um tipo de entidade, o EF Core ainda controlará os tipos de entidade contidos no resultado por padrão. Na consulta a seguir, que retorna um tipo anónimo, as instâncias de Blog serão rastreadas no conjunto de resultados.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, PostCount = b.Posts.Count() });

Se o conjunto de resultados contiver tipos de entidade provenientes da composição do LINQ, o EF Core irá rastreá-los.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });

Se o conjunto de resultados não contiver nenhum tipo de entidade, nenhum rastreamento será feito. Na consulta a seguir, retornamos um tipo anônimo com alguns dos valores da entidade (mas nenhuma instância do tipo de entidade real). Não há entidades rastreadas resultantes da consulta.

var blog = context.Blogs
    .Select(
        b =>
            new { Id = b.BlogId, b.Url });

O EF Core suporta a avaliação do cliente na projeção de nível superior. Se o EF Core materializar uma instância de entidade para avaliação do cliente, ela será rastreada. Aqui, como estamos passando blog entidades para o método StandardizeURLcliente, o EF Core também rastreará as instâncias do blog.

var blogs = await context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
    .ToListAsync();
public static string StandardizeUrl(Blog blog)
{
    var url = blog.Url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

O EF Core não rastreia as instâncias de entidade sem chave contidas no resultado. Mas o EF Core rastreia todas as outras instâncias dos tipos de entidade com uma chave, segundo as regras mencionadas acima.

Versões anteriores

Antes da versão 3.0, o EF Core tinha algumas diferenças na forma como o rastreamento era feito. As diferenças notáveis são as seguintes:

  • Conforme explicado na página Avaliação Cliente vs Servidor , o EF Core suportava a avaliação do cliente em qualquer parte da consulta antes da versão 3.0. A avaliação dos clientes provocou a materialização de entidades, que não fizeram parte do resultado. Assim, o EF Core analisou o resultado para detetar o que rastrear. Este desenho tinha algumas diferenças, como se segue:

    • A avaliação do cliente no processo de projeção, que levou à materialização, mas não devolveu a instância da entidade materializada, não foi devidamente rastreada. O exemplo a seguir não rastreou blog entidades.

      var blogs = await context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(
              blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
          .ToListAsync();
      
    • O EF Core não rastreou os objetos que saíam da composição do LINQ em certos casos. O exemplo a seguir não rastreou Post.

      var blog = context.Blogs
          .Select(
              b =>
                  new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });
      
  • Sempre que os resultados da consulta continham tipos de entidade sem chave, toda a consulta não era rastreada. Isso significa que os tipos de entidade com chaves, que estão no resultado, também não estavam sendo rastreados.

  • O EF Core costumava fazer resolução de identidade em consultas sem rastreamento. Utilizou referências fracas para acompanhar entidades que já tinham sido devolvidas. Portanto, se um conjunto de resultados contivesse a mesma entidade várias vezes, você obteria a mesma instância para cada ocorrência. Se um resultado anterior com a mesma identidade saísse do escopo e fosse coletado pelo coletor de lixo, o EF Core retornaria uma nova instância.