Quando estou em um cenário desanexado e obtenho um dto do cliente que mapeio em uma entidade para salvá-lo, faço o seguinte:
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
Pois o que é então o DbSet.Attach(entity)
ou por que devo usar o método .Attach quando EntityState.Modified já anexa a entidade?
c#
entity-framework
entity-framework-6
Elisabeth
fonte
fonte
Respostas:
Ao fazer isso
context.Entry(entity).State = EntityState.Modified;
, você não está apenas anexando a entidade aoDbContext
, mas também marcando toda a entidade como suja. Isso significa que quando você fizer issocontext.SaveChanges()
, o EF gerará uma instrução de atualização que atualizará todos os campos da entidade.Isso nem sempre é desejado.
Por outro lado,
DbSet.Attach(entity)
anexa a entidade ao contexto sem marcá-la como suja. É equivalente a fazercontext.Entry(entity).State = EntityState.Unchanged;
Ao anexar dessa forma, a menos que você prossiga para atualizar uma propriedade na entidade, na próxima vez que você chamar
context.SaveChanges()
, o EF não gerará uma atualização de banco de dados para esta entidade.Mesmo se você estiver planejando fazer uma atualização em uma entidade, se a entidade tiver muitas propriedades (colunas de banco de dados), mas você quiser atualizar apenas algumas, pode achar vantajoso fazer uma
DbSet.Attach(entity)
e, em seguida, atualizar apenas algumas propriedades que precisam de atualização. Fazer isso dessa forma gerará uma instrução de atualização mais eficiente do EF. EF só atualizará as propriedades que você modificou (em contraste com ocontext.Entry(entity).State = EntityState.Modified;
que fará com que todas as propriedades / colunas sejam atualizadas)Documentação relevante: Adicionar / Anexar e Estados de Entidade .
Exemplo de código
Digamos que você tenha a seguinte entidade:
Se o seu código for assim:
O SQL gerado será semelhante a este:
Observe como a instrução de atualização acima atualizará todas as colunas, independentemente de você ter realmente alterado os valores ou não.
Em contraste, se seu código usa o anexo "normal" como este:
Então, a instrução de atualização gerada é diferente:
Como você pode ver, a instrução update atualiza apenas os valores que foram realmente alterados depois que você anexou a entidade ao contexto. Dependendo da estrutura da sua tabela, isso pode ter um impacto positivo no desempenho.
Agora, qual opção é melhor para você depende inteiramente do que você está tentando fazer.
fonte
WHERE
cláusula contendo apenas a chave primária e sem nenhuma verificação de simultaneidade. Para ter verificação de simultaneidade, preciso configurar explicitamente uma coluna como um token de simultaneidade ou rowVersion. Nesse caso, aWHERE
cláusula terá apenas a chave primária e a coluna do token de simultaneidade, não todos os campos. Se seus testes mostrarem o contrário, eu adoraria ouvir sobre isso.DbContext.Entry(person).CurrentValues
eDbContext.Entry(person).OriginalValues
.Quando você usa o
DbSet.Update
método, o Entity Framework marca todas as propriedades de sua entidade comoEntityState.Modified
, portanto, as controla. Se você quiser alterar apenas algumas de suas propriedades, não todas, useDbSet.Attach
. Este método cria todas as suas propriedadesEntityState.Unchanged
, portanto, você deve fazer as propriedades que deseja atualizarEntityState.Modified
. Portanto, quando o aplicativo atinge oDbContext.SaveChanges
, ele operará apenas as propriedades modificadas.fonte
Além disso (para a resposta marcada), há uma diferença importante entre
context.Entry(entity).State = EntityState.Unchanged
econtext.Attach(entity)
(no EF Core):Fiz alguns testes para entender melhor por mim mesmo (portanto, isso também inclui alguns testes de referência geral), então este é o meu cenário de teste:
QueryTrackingBehavior.NoTracking
Estes são os modelos:
Estes são os dados de teste (originais) no banco de dados:
Para obter o pedido:
Agora os testes:
Atualização simples com EntityState :
Atualização simples com anexo :
Atualização com a alteração de Child-Ids com EntityState :
Atualização com a mudança de Child-Ids com Anexar :
Observação: isso gera exceção, não importa se o Id foi alterado ou foi definido com o valor original, parece que o estado do Id está definido como "alterado" e isso não é permitido (porque é a chave primária)
Atualize com a alteração de Child-Ids como novos (sem diferença entre EntityState e Attach):
Nota: Veja a diferença para Atualizar com EntityState sem novo (acima). Desta vez o Nome será atualizado, devido à nova instância do Usuário.
Atualize com a alteração dos Reference-Ids com EntityState :
Atualização com a mudança da referência-Ids com Anexar :
Nota: A referência será alterada para o Usuário 3, mas também o usuário 1 será atualizado, acho que isso ocorre porque o
order.OrderedByUser.Id
está inalterado (ainda é 1).Conclusão Com EntityState você tem mais controle, mas precisa atualizar as subpropriedades (segundo nível) por conta própria. Com o Attach você pode atualizar tudo (acho que com todos os níveis de propriedades), mas você tem que ficar de olho nas referências. Apenas por exemplo: Se User (OrderedByUser) fosse um dropDown, alterar o valor por meio de dropDown poderia substituir todo o objeto User. Nesse caso, o dropDown-Value original seria sobrescrito em vez da referência.
Para mim, o melhor caso é definir objetos como OrderedByUser como nulo e definir apenas o order.OrderedByUserId para o novo valor, se eu quiser apenas alterar a referência (não importa se EntityState ou Attach).
Espero que ajude, sei que é muito texto: D
fonte