Em poucas palavras, a exceção é lançada durante o modelo de wrapper POSTing e alterando o estado de uma entrada para 'Modificado'. Antes de alterar o estado, o estado é definido como 'Desconectado', mas chamar Attach () gera o mesmo erro. Eu estou usando EF6.
Encontre meu código abaixo (os nomes dos modelos foram alterados para facilitar a leitura)
Modelo
// Wrapper classes
public class AViewModel
{
public A a { get; set; }
public List<B> b { get; set; }
public C c { get; set; }
}
Controlador
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if (!canUserAccessA(id.Value))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
var aViewModel = new AViewModel();
aViewModel.A = db.As.Find(id);
if (aViewModel.Receipt == null)
{
return HttpNotFound();
}
aViewModel.b = db.Bs.Where(x => x.aID == id.Value).ToList();
aViewModel.Vendor = db.Cs.Where(x => x.cID == aViewModel.a.cID).FirstOrDefault();
return View(aViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(AViewModel aViewModel)
{
if (!canUserAccessA(aViewModel.a.aID) || aViewModel.a.UserID != WebSecurity.GetUserId(User.Identity.Name))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
if (ModelState.IsValid)
{
db.Entry(aViewModel.a).State = EntityState.Modified; //THIS IS WHERE THE ERROR IS BEING THROWN
db.SaveChanges();
return RedirectToAction("Index");
}
return View(aViewModel);
}
Como mostrado acima da linha
db.Entry(aViewModel.a).State = EntityState.Modified;
lança exceção:
Falha ao anexar uma entidade do tipo 'A' porque outra entidade do mesmo tipo já possui o mesmo valor de chave primária. Isso pode acontecer ao usar o método 'Anexar' ou ao definir o estado de uma entidade como 'Inalterado' ou 'Modificado' se alguma entidade no gráfico tiver valores-chave conflitantes. Isso pode ocorrer porque algumas entidades são novas e ainda não receberam valores-chave gerados pelo banco de dados. Nesse caso, use o método 'Adicionar' ou o estado da entidade 'Adicionado' para rastrear o gráfico e defina o estado de entidades não novas como 'Inalterado' ou 'Modificado', conforme apropriado.
Alguém vê algo errado no meu código ou entende em que circunstâncias isso geraria esse erro durante a edição de um modelo?
fonte
EntityState
? Como sua entidade vem de uma solicitação post, ele não deve ser monitorado pelo contexto atual, eu acho que considera que tentar adicionar um item com um ID existentedb
instância for a mesma entre suas duas ações, ela poderá explicar seu problema, pois o item será carregado pelo método GET (depois rastreado pelo contexto) e poderá não reconhecer o item no método POST como a entidade buscada antes .canUserAccessA()
carregar a entidade direta ou como uma relação de outra entitiy?Respostas:
Problema resolvido!
Attach
O método poderia potencialmente ajudar alguém, mas não ajudaria nessa situação, pois o documento já estava sendo rastreado durante o carregamento na função do controlador Edit GET. Anexar lançaria exatamente o mesmo erro.O problema que encontrei aqui foi causado pela função
canUserAccessA()
que carrega a entidade A antes de atualizar o estado do objeto a. Isso estava estragando a entidade rastreada e estava mudando o estado de um objeto paraDetached
.A solução foi alterar
canUserAccessA()
para que o objeto que eu estava carregando não fosse rastreado. A funçãoAsNoTracking()
deve ser chamada durante a consulta do contexto.Por alguma razão, eu não poderia usá
.Find(aID)
-AsNoTracking()
lo, mas isso realmente não importa, pois eu poderia conseguir o mesmo alterando a consulta.Espero que isso ajude alguém com problema semelhante!
fonte
using System.Data.Entity;
usarAsNoTracking()
.Curiosamente:
Ou se você ainda não é genérico:
parece ter resolvido meu problema sem problemas.
fonte
AddOrUpdate
é um método de extensão noSystem.Data.Entity.Migrations
espaço para nome.Parece que a entidade que você está tentando modificar não está sendo rastreada corretamente e, portanto, não é reconhecida como editada, mas adicionada.
Em vez de definir diretamente o estado, tente fazer o seguinte:
Além disso, gostaria de avisar que seu código contém uma potencial vulnerabilidade de segurança. Se você estiver usando uma entidade diretamente no seu modelo de exibição, corre o risco de alguém modificar o conteúdo da entidade adicionando campos nomeados corretamente no formulário enviado. Por exemplo, se o usuário incluísse a caixa de entrada com o nome "A.FirstName" e a entidade contivesse esse campo, o valor seria vinculado ao viewmodel e salvo no banco de dados, mesmo que o usuário não tivesse permissão para alterá-lo na operação normal do aplicativo .
Atualizar:
Para superar a vulnerabilidade de segurança mencionada anteriormente, você nunca deve expor seu modelo de domínio como seu modelo de exibição, mas use um modelo de exibição separado. Em seguida, sua ação receberá um modelo de visualização que você poderá mapear de volta para o modelo de domínio usando alguma ferramenta de mapeamento como o AutoMapper. Isso impediria a modificação de dados confidenciais pelo usuário.
Aqui está uma explicação estendida:
http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/
fonte
Tente o seguinte:
fonte
para mim, a cópia local foi a fonte do problema. isso resolveu
fonte
Meu caso foi que eu não tinha acesso direto ao contexto EF do meu aplicativo MVC.
Portanto, se você estiver usando algum tipo de repositório para persistência da entidade, pode ser apropriado desanexar a entidade carregada explicitamente e definir EntityState vinculado como Modificado.
Código de amostra (abstrato):
MVC
Repositório
fonte
Pensei em compartilhar minha experiência com essa, apesar de me sentir um pouco boba por não ter percebido antes.
Estou usando o padrão de repositório com as instâncias de repositório injetadas nos meus controladores. Os repositórios concretos instanciam meu ModelContext (DbContext), que dura a vida útil do repositório, que é
IDisposable
e é descartado pelo controlador.O problema para mim foi que eu tenho uma versão modificada de carimbo e linha em minhas entidades, então eu as estava obtendo primeiro para comparar com os cabeçalhos de entrada. Obviamente, isso carregou e acompanhou a entidade que estava sendo atualizada posteriormente.
A correção foi simplesmente mudar o repositório de atualizar um contexto uma vez no construtor para ter os seguintes métodos:
Isso permite que os métodos do repositório renovem sua instância de contexto a cada uso chamando
GetDbContext
ou use uma instância anterior, se assim o desejarem, especificando true.fonte
Eu adicionei esta resposta apenas porque o problema é explicado com base em padrões de dados mais complexos e achei difícil de entender aqui.
Eu criei um aplicativo bastante simples. Este erro ocorreu dentro da ação Editar POST. A ação aceitou o ViewModel como um parâmetro de entrada. O motivo para usar o ViewModel foi fazer algum cálculo antes de o registro ser salvo.
Depois que a ação passou pela validação, como
if(ModelState.IsValid)
, meu erro foi projetar valores do ViewModel em uma instância completamente nova do Entity. Eu pensei que teria que criar uma nova instância para armazenar dados atualizados e salvá-la.O que eu percebi mais tarde foi que eu tinha que ler o registro do banco de dados:
e atualizou este objeto. Tudo funciona agora.
fonte
Eu tive esse problema com var local e eu apenas desanexá-lo assim:
Causas problemáticas de objetos carregados com a mesma chave; portanto, primeiro desanexaremos esse objeto e faremos a atualização para evitar conflitos entre dois objetos com a mesma chave
fonte
Eu tive um problema semelhante, depois de pesquisar por 2-3 dias, encontrado ".AsNoTracking" deve ser removido, pois a EF não rastreia as alterações e assume que não há alterações, a menos que um objeto seja anexado. Além disso, se não usarmos .AsNoTracking, a EF saberá automaticamente qual objeto salvar / atualizar, para que não haja necessidade de usar Anexar / Adicionado.
fonte
Use
AsNoTracking()
onde você está obtendo sua consulta.fonte
Encontrei este erro em que
Mudei o método B para ter uma instrução using e confiar apenas no db2 local . Depois de:
fonte
Semelhante ao que Luke Puplett está dizendo, o problema pode ser causado por não descartar ou criar adequadamente o seu contexto.
No meu caso, eu tive uma classe que aceitou um contexto chamado
ContextService
:Meu serviço de contexto tinha uma função que atualiza uma entidade usando um objeto de entidade instanciado:
Tudo isso estava bem, meu controlador onde eu inicializei o serviço foi o problema. Meu controlador originalmente era assim:
Eu mudei para isso e o erro desapareceu:
fonte
Este problema também pode ser visto durante
ViewModel
aEntityModel
mapeamento (usandoAutoMapper
, etc.) e tentando incluemcontext.Entry().State
econtext.SaveChanges()
como um bloco utilizando como mostrado abaixo resolveria o problema. Por favor, lembre-se de que ocontext.SaveChanges()
método é usado duas vezes em vez de usar logo após,if-block
pois também deve ser usado no bloco.Espero que isto ajude...
fonte
Aqui o que eu fiz no caso semelhante.
Essa situação significa que a mesma entidade já existia no contexto.
Primeiro verifique no ChangeTracker se a entidade está no contexto
Se existir
fonte
Eu mantenho para corrigir o problema, atualizando o estado. quando você aciona a localização ou qualquer outra operação de consulta no mesmo estado de registro foi atualizada com modificada, precisamos definir o status como Desanexado, para que você possa acionar sua alteração de atualização
fonte
Eu resolvo esse problema com um bloco "using"
Aqui é onde eu recebo a ideia https://social.msdn.microsoft.com/Forums/sqlserver/es-ES/b4b350ba-b0d5-464d-8656-8c117d55b2af/problema-al-modificar-en-entity-framework?forum = vcses está em espanhol (procure a segunda resposta)
fonte
você pode usar um método adicionado como;
mas, em muitos casos, se você desejar usar mais de um modelo nesse momento, isso não funcionará porque a entidade já está anexada a outra entidade. Portanto, nesse momento, você pode usar o método de migração de entidades ADDOrUpdate, que simplesmente migra um objeto de um para outro e, como resultado, você não recebe nenhum erro.
fonte
Limpar tudo Estado
dbContextGlobalERP.ChangeTracker.Entries (). Where (e => e.Entity! = null) .ToList (). ForEach (e => e.State = EntityState.Detached);
fonte