2012-03-06 18 views
16

Sto tentando di implementare un AuditLog utilizzando l'oggetto ChangeTracker di DBContext, ho riscontrato un problema in cui lo DbEntityEntry.OriginalValues veniva cancellato e sostituito con DbEntityEntry.CurrentValues. È stato portato alla mia attenzione che il problema era come stavo aggiornando l'oggetto che veniva monitorato nel DbContext (post originale: Entity Framework DbContext SaveChanges() OriginalValue Incorrect).EF 4: Come aggiornare correttamente l'oggetto in DbContext utilizzando MVC con pattern di repository

Quindi ora ho bisogno di aiuto sul modo corretto di aggiornare un oggetto persistente utilizzando il modello di repository in MVC 3 con Entity Framework 4. Questo codice di esempio viene adattato dall'applicazione SportsStore nel libro Pro Asp.NET MVC 3 Framework messo fuori da Apress.

Ecco il mio 'Modifica' azione posta in AdminController:

[HttpPost] 
public ActionResult Edit(Product product) 
{ 
    if (ModelState.IsValid) 
    { 
     // Here is the line of code of interest... 
     repository.SaveProduct(product, User.Identity.Name); 

     TempData["message"] = string.Format("{0} has been saved", product.Name); 
     return RedirectToAction("Index"); 
    } 
    else 
    { 
     // there is something wrong with the data values 
     return View(product); 
    } 
} 

Questo chiama in EFProductRepository classe concreta (che sta attuando l'interfaccia IProductRepository e iniettato attraverso Ninject). Ecco il metodo SaveProduct nella classe repository concreta:

public void SaveProduct(Product product, string userID) 
{ 
    if (product.ProductID == 0) 
    { 
     context.Products.Add(product); 
    } 
    else 
    { 
     context.Entry(product).State = EntityState.Modified; 
    } 
    context.SaveChanges(userID); 
} 

Il problema (come è stato portato alla mia attenzione nel mio precedente SO post), è che quando context.Entry(product).State = EntityState.Modified; si chiama si scombina in qualche modo fino capacità del ChangeTracker di riferire in merito i cambiamenti. Quindi nel mio metodo DBContext.SaveChanges (stringa userID) sovraccarico, non vedo valori precisi nell'oggetto ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Modified).OriginalValues.

Se aggiorno il mio metodo EFProductRepository.SaveProduct a questo funziona:

public void SaveProduct(Product product, string userID) 
{ 
    if (product.ProductID == 0) 
    { 
     context.Products.Add(product); 
    } 
    else 
    { 
     Product prodToUpdate = context.Products 
      .Where(p => p.ProductID == product.ProductID).FirstOrDefault(); 

     if (prodToUpdate != null) 
     { 
      // Just updating one property to demonstrate.... 
      prodToUpdate.Name = product.Name; 
     } 
    } 
    context.SaveChanges(userID); 
} 

Vorrei sapere il modo corretto per aggiornare l'oggetto prodotto e persistono in questo scenario in modo tale che il ChangeTracker traccia accuratamente le mie modifiche alla classe POCO nel repository. Dovrei fare l'ultimo esempio (eccetto ovviamente copiare tutti i campi che potrebbero essere stati aggiornati) o dovrei adottare un approccio diverso?

In questo esempio la classe "Prodotto" è molto semplice e ha solo proprietà di stringa e proprietà decimali. Nella nostra applicazione reale avremo tipi "complessi" e le classi POCO faranno riferimento ad altri oggetti (ad esempio, Persona che ha un elenco di indirizzi). So che potrebbe anche essere necessario fare qualcosa di speciale per tenere traccia delle modifiche in questo caso. Forse la conoscenza di questo cambierà alcuni consigli che ricevo qui.

risposta

31

si scombina in qualche modo fino capacità del ChangeTracker di riferire in merito alle modifiche

No, non scombina nulla. L'abilità di modifica del tracker si basa sul fatto che il tracker delle modifiche conosce l'entità prima di apportare modifiche. Ma nel tuo caso il Change Tracker viene informato dell'entità con le modifiche già applicate e l'entità POCO non mantiene alcuna informazione sui suoi valori originali. L'entità POCO ha solo un singolo set di valori che viene interpretato sia come attuale che come originale. Se vuoi qualcos'altro, devi codificarlo da solo.

dovrei fare il secondo esempio

Nel tuo caso semplice sì e si può utilizzare semplicemente:

public void SaveProduct(Product product, string userID) 
{ 
    if (product.ProductID == 0) 
    { 
     context.Products.Add(product); 
    } 
    else 
    { 
     Product prodToUpdate = context.Products 
      .Where(p => p.ProductID == product.ProductID).FirstOrDefault(); 

     if (prodToUpdate != null) 
     { 
      context.Entry(prodToUpdate).CurrentValues.SetValues(product); 
     } 
    } 

    context.SaveChanges(userID); 
} 

Il problema è che questo funziona solo per le proprietà semplici e complessi . Un altro problema è che questo sovrascrive tutte le proprietà, quindi se per esempio la tua entità ha un campo che non vuoi mostrare nell'interfaccia utente (o non vuoi consentire all'utente di modificare il campo) devi comunque impostare il valore corrente corretto sul tuo product In caso contrario, quel valore verrà sovrascritto quando si applicano i valori correnti.

L'intera situazione diventa significantly more complex quando si tenta di applicare questo allo scenario reale. Fallirai e fallirai molte volte prima di scrivere un sacco di codice per supportare esattamente i tuoi casi perché probabilmente non esiste una soluzione generica EF non ha metodi di supporto per questi scenari. Il motivo è che EF dispone di una macchina a stati interni per ogni entità e alcune associazioni e che è necessario configurare lo stato per ogni singola entità o associazione che si desidera aggiornare, inserire o eliminare ed è necessario eseguirlo in conformità con le regole interne di EF. Lo stato di impostazione dell'entità cambierà lo stato di quell'entità singola ma non le sue relazioni.

Lo faccio semplicemente caricando l'entità corrente con tutte le relazioni dal database e manualmente (nel codice) unendo l'intero grafo dell'entità (è sufficiente avere un grafo di entità distaccato e allegato ed è necessario trasferire tutte le modifiche da distaccato a quello allegato).

+1

Grazie per la risposta. Hai un esempio del tuo approccio? Ho letto la domanda SO a cui ti sei collegato, ma non sono ancora del tutto chiaro. –

+4

Ladislav, mi sono appena reso conto che non avevo mai scelto questo come risposta. Hai risposto alla mia domanda postata originale e lo apprezzo. Inoltre, le molte altre risposte sullo stackoverflow alle domande EF sono state incredibilmente utili per me. Grazie ancora. –

Problemi correlati