Como Resolver o Erro de Entity Tracking no Entity Framework Core

O erro "The instance of entity type 'X' cannot be tracked because another instance with the same key value is already being tracked" é uma das exceçÔes mais comuns e frustrantes no Entity Framework Core. Vamos entender por que ela acontece e como resolver de vez.

VocĂȘ estĂĄ lĂĄ, programando tranquilamente. O cĂłdigo faz sentido, as entidades estĂŁo configuradas, as relaçÔes parecem corretas. VocĂȘ chama SaveChangesAsync() e... BUM. Uma exceção que parece vir do nada.

O erro que vamos resolver hoje Ă© este:

System.InvalidOperationException: 
The instance of entity type 'Produto' cannot be tracked because another instance 
with the same key value for {'Id'} is already being tracked. 
When attaching existing entities, ensure that only one entity instance 
with a given key value is attached.

Esse erro jĂĄ me tirou do sĂ©rio mais vezes do que eu gostaria de admitir. Mas, com o tempo, aprendi que ele tem causas bem especĂ­ficas — e soluçÔes igualmente especĂ­ficas.

O cenĂĄrio que causa o erro: um exemplo prĂĄtico

Imagine o seguinte cenĂĄrio: vocĂȘ tem uma entidade Produto, recebe uma atualização via API, e tenta salvar no banco.

A entidade:

public class Produto
{
    public int Id { get; set; }
    public string Nome { get; set; }
    public decimal Preco { get; set; }
    public int CategoriaId { get; set; }
    public Categoria Categoria { get; set; }
}

O cĂłdigo problemĂĄtico:

[HttpPut("{id}")]
public async Task AtualizarProduto(int id, Produto produtoAtualizado)
{
    // 1. Carrega o produto existente
    var produtoExistente = await _context.Produtos
        .Include(p => p.Categoria)
        .FirstOrDefaultAsync(p => p.Id == id);

    // 2. Tenta atualizar os valores
    produtoExistente.Nome = produtoAtualizado.Nome;
    produtoExistente.Preco = produtoAtualizado.Preco;
    produtoExistente.Categoria = produtoAtualizado.Categoria; // <- PROBLEMA AQUI!

    // 3. Salva
    await _context.SaveChangesAsync(); // BUM! Exceção de tracking!
}

Quando a requisição chega, o produtoAtualizado contém uma instùncia de Categoria com o mesmo Id que jå estå sendo rastreada pelo produtoExistente (que foi carregado com Include()). O EF Core entra em conflito: duas instùncias diferentes da mesma entidade com a mesma chave.

? ERRO: System.InvalidOperationException: 
The instance of entity type 'Categoria' cannot be tracked because another instance 
with the key value '{Id: 5}' is already being tracked.

Solução 1: Atualize a entidade rastreada, não a instùncia recebida

Em vez de anexar a instĂąncia recebida, carregue a entidade original do banco e atualize suas propriedades.

[HttpPut("{id}")]
public async Task AtualizarProduto(int id, Produto produtoAtualizado)
{
    // 1. Carrega o produto do banco
    var produtoExistente = await _context.Produtos
        .Include(p => p.Categoria)
        .FirstOrDefaultAsync(p => p.Id == id);

    if (produtoExistente == null)
        return NotFound();

    // 2. Atualiza APENAS as propriedades que vieram na requisição
    // NÃO atribui a Categoria inteira — apenas o Id
    produtoExistente.Nome = produtoAtualizado.Nome;
    produtoExistente.Preco = produtoAtualizado.Preco;
    produtoExistente.CategoriaId = produtoAtualizado.CategoriaId;

    // 3. Salva
    await _context.SaveChangesAsync();

    return Ok(produtoExistente);
}
? SUCESSO: Produto atualizado com sucesso! 
Nenhum conflito de tracking porque usamos a mesma instĂąncia rastreada.

Solução 2: Use AsNoTracking() para consultas que não vão modificar dados

Se vocĂȘ sĂł precisa ler dados e nĂŁo vai modificĂĄ-los, use AsNoTracking(). Isso impede que o DbContext rastreie a entidade.

// Consulta para exibição (NÃO vai modificar)
public async Task> ListarProdutos()
{
    // AsNoTracking() previne tracking desnecessĂĄrio
    return await _context.Produtos
        .AsNoTracking()
        .Include(p => p.Categoria)
        .ToListAsync();
}
? SUCESSO: 150 produtos retornados.
Nenhuma entidade foi rastreada. MemĂłria otimizada!

Solução 3: Detach + Update para substituir a entidade

Se vocĂȘ sabe que a entidade rastreada estĂĄ desatualizada e quer substituĂ­-la completamente, pode desanexar a antiga e anexar a nova.

public async Task SubstituirProduto(Produto produtoNovo)
{
    // 1. Verifica se a entidade jĂĄ estĂĄ sendo rastreada
    var entrada = _context.Entry(produtoNovo);
    
    if (entrada.State != EntityState.Detached)
    {
        // 2. Desanexa a entidade antiga
        entrada.State = EntityState.Detached;
        Console.WriteLine($"?? Entidade {produtoNovo.Id} desanexada.");
    }

    // 3. Anexa como modificada
    _context.Update(produtoNovo);
    Console.WriteLine($"?? Entidade {produtoNovo.Id} anexada como Modified.");

    // 4. Salva
    await _context.SaveChangesAsync();
    Console.WriteLine($"? Produto {produtoNovo.Id} salvo com sucesso!");
}
?? Entidade 42 desanexada.
?? Entidade 42 anexada como Modified.
? Produto 42 salvo com sucesso!

Solução 4: CurrentValues.SetValues() — a abordagem mais segura

Esta é a abordagem recomendada para cenårios de atualização. Ela copia os valores da instùncia recebida para a instùncia rastreada sem conflitos.

public async Task AtualizarProdutoComSetValues(Produto produtoAtualizado)
{
    // 1. Carrega a entidade rastreada
    var produtoOriginal = await _context.Produtos
        .FirstOrDefaultAsync(p => p.Id == produtoAtualizado.Id);

    if (produtoOriginal == null)
    {
        Console.WriteLine($"? Produto {produtoAtualizado.Id} nĂŁo encontrado.");
        return;
    }

    Console.WriteLine($"?? Produto original: {produtoOriginal.Nome} (R$ {produtoOriginal.Preco})");

    // 2. Aplica os novos valores Ă  instĂąncia rastreada
    _context.Entry(produtoOriginal).CurrentValues.SetValues(produtoAtualizado);

    Console.WriteLine($"?? Valores atualizados: {produtoAtualizado.Nome} (R$ {produtoAtualizado.Preco})");

    // 3. Salva
    await _context.SaveChangesAsync();

    Console.WriteLine($"? Produto {produtoOriginal.Id} atualizado com sucesso!");
}
?? Produto original: Teclado MecĂąnico (R$ 350,00)
?? Valores atualizados: Teclado MecĂąnico RGB (R$ 399,00)
? Produto 42 atualizado com sucesso!

Como diagnosticar: habilitando logs sensitivos

A prĂłpria Microsoft recomenda ativar o EnableSensitiveDataLogging para ver quais chaves estĂŁo causando conflito.

// Configure no DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer("Server=localhost;Database=MeuDB;")
        .EnableSensitiveDataLogging() // ATENÇÃO: APENAS EM DESENVOLVIMENTO!
        .LogTo(Console.WriteLine);
}

// Agora a mensagem de erro mostra o valor exato:
// "The instance of entity type 'Categoria' cannot be tracked because 
// another instance with the key value '{Id: 5}' is already being tracked."

?? Atenção

Ative o logging sensitivo apenas em desenvolvimento. Nunca em produção, pois ele pode expor dados sensíveis como senhas e chaves privadas.

Resumo rĂĄpido: o que fazer quando o erro aparecer

Situação Solução
Consulta apenas para leitura AsNoTracking()
Atualização com objeto recebido CurrentValues.SetValues()
Substituição completa Detach + Update
Conflito de navegação Atualize apenas o Id da navegação
DiagnĂłstico EnableSensitiveDataLogging()

O sistema de tracking do Entity Framework Core Ă© uma das suas maiores vantagens. Mas, como toda ferramenta poderosa, exige cuidado. O erro "instance cannot be tracked" Ă© um sinal de que vocĂȘ estĂĄ tentando usar o DbContext de forma inconsistente.

Com as soluçÔes que mostrei aqui, vocĂȘ vai gastar menos tempo debugando e mais tempo construindo coisas incrĂ­veis.

E vocĂȘ, jĂĄ passou por esse erro? Deixe seu comentĂĄrio contando como resolveu — sua experiĂȘncia pode ajudar outros devs.

Quer mais dicas de C# e .NET?

No Nerd Cult, acreditamos que cĂłdigo de qualidade resolve problemas reais. Assine nossa newsletter e receba conteĂșdos sobre desenvolvimento, carreira e cultura geek.

Livros sobre C e Net Core

Livros sobre C# e .Net Core

O melhor conteĂșdo para o aprendizado da linguagem.

Consulte o preço no site
(150 avaliaçÔes)
Amazon
Comprar Agora*Link de Parceiro

Quer contribuir com este blog?

Adoramos ouvir a opiniĂŁo dos nossos leitores! VocĂȘ jĂĄ passou por esse erro no EF Core? Como vocĂȘ resolveu? Deixe seu comentĂĄrio abaixo com sua experiĂȘncia — suas dicas podem ajudar outros devs na mesma situação. Sugira novos temas que vocĂȘ gostaria de ver no Nerd Cult — e se vocĂȘ quiser ver seu prĂłprio post publicado aqui, entre em contato conosco! Estamos sempre abertos a colaboraçÔes e histĂłrias inspiradoras da nossa comunidade.

Nerd Cult — onde o cĂłdigo encontra o rock, o cinema e a cultura geek. Porque ser nerd Ă© transformar o medo em curiosidade, e a curiosidade em poder.

#EntityFrameworkCore #CSharp #DotNet #Desenvolvimento #NerdCult

Este conteĂșdo foi Ăștil para vocĂȘ?

ComentĂĄrios (0)

Nenhum comentĂĄrio ainda. Seja o primeiro a comentar!