2015-06-07 10 views
5

Sto cercando di aggiornare un record utilizzando Entity Framework 6, code-first, mappatura fluente o uno strumento come Automapper.L'aggiornamento dei dati esistenti in EF 6 genera un'eccezione: "... entità dello stesso tipo ha già lo stesso valore di chiave primaria."

L'entità (Employee) presenta altre strutture composite ad esso associati come Addreess (raccolta), Department

È anche ereditato da una base chiamata User

il metodo Save è il seguente, con _dbContext essendo l'implementazione DbConext

 public bool UpdateEmployee(Employee employee) 
     { 
      var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault(); 
      if (entity == null) 
      { 
       _dbContext.Employees.Add(employee); 
      } 
      else 
      { 
       _dbContext.Entry(employee).State = EntityState.Modified; // <- Exception raised here 
       _dbContext.Employees.Attach(employee); 

      } 

      return _dbContext.SaveChanges() > 0; 

     } 

continuo a ricevere l'errore:

Attaching an entity of type failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

Ho provato quanto segue:

  1. Collegamento prima di impostare a EntityState.Modified
  2. Aggiunta AsNoTracking() sulla query se l'oggetto esiste (Non fa eccezione, ma DB non è aggiornato) - https://stackoverflow.com/a/23228001/919426
  3. risparmio utilizzando l'entità di base _dbContext.Users invece dell'entità Dipendente - https://stackoverflow.com/a/25575634/919426

Nessuno dei quali funziona per me ora.

Cosa potrei aver sbagliato per alcune di quelle soluzioni non funzionare nella mia situazione?

+0

use 'var entity = _dbContext.Employees.FirstOrDefault (c => c.Id == employee.Id);' – dotctor

+0

Qual è la durata di 'dbContext'? Scommetto che non crei un nuovo contesto abbastanza spesso. –

+0

Si prega di dare un'occhiata alla mia risposta su [ASP.NET MVC - Il collegamento di un'entità di tipo "MODELNAME" non è riuscito perché un'altra entità dello stesso tipo ha già lo stesso valore di chiave primaria] (http://stackoverflow.com/questions/ 23201907/asp-net-MVC-allegando-an-soggetto-di-tipo-ModelName-failed-perché-altro-ent/39557606 # 39557606). –

risposta

17

EF comprende già un modo per mappare le proprietà senza ricorrere a Automapper, a patto che non si dispone di proprietà di navigazione per aggiornare:

public bool UpdateEmployee(Employee employee) 
    { 
     var entity = _dbContext.Employees.Where(c => c.Id == employee.Id).AsQueryable().FirstOrDefault(); 
     if (entity == null) 
     { 
      _dbContext.Employees.Add(employee); 
     } 
     else 
     { 
      _dbContext.Entry(entity).CurrentValues.SetValues(employee);    
     } 

     return _dbContext.SaveChanges() > 0; 

    } 

Questo genera di solito un'istruzione SQL meglio dato che si aggiornerà solo le proprietà che sono cambiati.

Se si desidera continuare a utilizzare il metodo originale, avrete sbarazzarsi di entity dal contesto, sia utilizzando AsNoTracking (non so perché non è stato aggiornato nel tuo caso, dovrebbe avere alcun effetto, quindi il problema potrebbe essere qualcos'altro) o come modificare la query per impedire che materializzi l'entità in primo luogo, utilizzando ad esempio qualcosa come bool exists = dbContext.Employees.Any(c => c.Id == employee.Id).

+0

Ho detto che non sto usando Automapper. Ho delle proprietà di navigazione (nelle classi composte ike 'Address' e' Department'. Stai dicendo che ho bisogno di Automapper allora ?. Il tuo codice non ha provocato un'eccezione ma il DB non è stato aggiornato – user919426

+0

Stai modificando le proprietà dell'oggetto o qualcos'altro? Se stai modificando un intero oggetto grafico, dovrai passare attraverso quel grafico e contrassegnare tutti gli elementi come modificati. – ESG

+0

Ho un record 'Dipendente' che vorrei aggiornare usando Entiy Framework (nuovo Per quanto ho riserchiato, il metodo che ho usato dovrebbe funzionare Non so dove è coinvolto il grafico degli oggetti nel processo? Spiegare l'alternativa, dato il tuo ultimo commento.Ho un oggetto 'Dipendente' con la navigazione Proprietà. Desidero aggiornare SOLO QUALE record'Employee' ... Come è fatto? e quale potrebbe essere l'errore che ho ottenuto usando il metodo che sto usando? – user919426

2

È necessario staccare per evitare duplicati eccezione chiave primaria whist invocando SaveChanges

db.Entry(entity).State = EntityState.Detached; 
4

Questo ha funzionato per me

var aExists = _db.Model.Find(newOrOldOne.id); 
if(aExists==null) 
{ 
    _db.Model.Add(newOrOldOne); 
} 
else 
{ 
    _db.Entry(aExists).State = EntityState.Detached; 
    _db.Entry(newOrOldOne).State = EntityState.Modified; 
} 
3

che ho incontrato la stessa cosa quando si usa un repository e unità di lavoro modello (come documentato nello mvc4 with ef5 tutorial).

Il repository generico contiene un metodo di aggiornamento (TEntity) che tenta di collegare quindi impostare Entry.Stato = modificato. La 'risposta' sopra-votata sopra non risolve questo se si intende attenersi al modello uow/repo.

Ho tentato di utilizzare il processo di scollegamento prima del collegamento, ma ha comunque fallito per lo stesso motivo indicato nella domanda iniziale.

La ragione di questo, si scopre, è che stavo controllando per vedere se esistesse un record, quindi usando automapper per generare un oggetto entità dal mio dto prima di chiamare update().

Controllando l'esistenza di quel record, ho messo l'oggetto entità in ambito, e non è stato in grado di staccarlo (che è anche il motivo per cui l'interrogante iniziale non è stato in grado di staccare) ... Tt tracciato il record e non ha permesso modifiche dopo che ho automapperato il dto in un'entità e poi ho tentato di aggiornare.

Ecco l'implementazione del pronti contro termine generico di aggiornamento:

public virtual void Update(TEntity entityToUpdate) 
{ 
    dbSet.Attach(entityToUpdate); 
    context.Entry(entityToUpdate).State = EntityState.Modified; 
} 

Questo è il mio metodo PUT (sto usando WebAPI con angolare)

[HttpPut] 
public IHttpActionResult Put(int id, Product product) 
{ 
    IHttpActionResult ret; 
    try 
    { 
     // remove pre-check because it locks the record 
     // var e = unitOfWork.ProductRepository.GetByID(id); 
     // if (e != null) { 
     var toSave = _mapper.Map<ProductEntity>(product); 
     unitOfWork.ProductRepository.Update(toSave); 
     unitOfWork.Save(); 
     var p = _mapper.Map<Product>(toSave); 
     ret = Ok(p); 
     // } 
     // else 
     // ret = NotFound(); 
    } 
    catch (DbEntityValidationException ex) 
    { 
     ret = BadRequest(ValidationErrorsToMessages(ex)); 
    } 
    catch (Exception ex) 
    { 
     ret = InternalServerError(ex); 
    } 
    return ret; 
} 

Come potete vedere, ho commentato fuori il mio controllo per vedere se il record esiste. Immagino che vedrò come funziona se tento di aggiornare un record che non esiste più, poiché non ho più un'opportunità di ritorno di NotFound().

Quindi, per rispondere alla domanda iniziale, direi di non cercare l'entità == null prima di tentare o di escogitare un'altra metodologia. forse nel mio caso, potrei disporre del mio UnitOfWork dopo aver scoperto l'oggetto e poi fare il mio aggiornamento.

Problemi correlati