2012-03-06 5 views
6

Próbuję zaimplementować auditlog używając EF 4.1, poprzez nadpisanie SaveChanges() metody, jak to omówiono w następujących miejscach:Entity Framework DbContext SaveChanges() OriginalValue Nieprawidłowa

Mam problemy z wpisami "zmodyfikowanymi". Ilekroć próbuję uzyskać w danej właściwości OriginalValue, zawsze ma ona tę samą wartość, co w polu CurrentValue.

raz pierwszy użyć tego kodu, i to skutecznie identyfikuje wpisy, które są modyfikowane:

public int SaveChanges(string userID) 
{ 

    // Have tried both with and without the following line, and received same results: 
    // ChangeTracker.DetectChanges(); 

    foreach (
     var ent in this.ChangeTracker 
        .Entries() 
        .Where(p => p.State == System.Data.EntityState.Added 
            p.State == System.Data.EntityState.Deleted    
            p.State == System.Data.EntityState.Modified)) 
    { 
     // For each change record, get the audit record entries and add them 
     foreach (AuditLog log in GetAuditRecordsForChange(ent, userID)) 
     { 
      this.AuditLog.Add(log); 
     } 

    } 

    return base.SaveChanges(); 
} 

Problem polega na tym (kod skróconej):

private List<AuditLog> GetAuditRecordsForChange(DbEntityEntry dbEntry, string userID) 
    { 
     if (dbEntry.State == System.Data.EntityState.Modified) 
     { 
      foreach (string propertyName in dbEntry.OriginalValues.PropertyNames) 
      { 
       if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), 
        dbEntry.CurrentValues.GetValue<object>(propertyName))) 
       { 
         // It never makes it into this if block, even when 
         // the property has been updated. 
       } 

       // If I updated the property "Name" which was originally "OldName" to the value "NewName" and then break here and inspect the values by calling: 
       //  ?dbEntry.OriginalValues.GetValue<object>("Name").ToString() 

       // the result will be "NewName" and not "OldName" as expected 
      } 
     } 
    } 

Najdziwniejsze jest to, że zadzwoń pod dbEntry.Property(propertyName).IsModified(); będzie zwraca true w tym przypadku. Po prostu wartość OriginalValue nie ma oczekiwanej wartości w środku. Czy ktoś byłby chętny do wskazania mi właściwego kierunku? Nie wydaje mi się, żeby to działało poprawnie.

+0

W jaki sposób wyszukujesz swoje jednostki, a następnie zmieniasz wartości właściwości? Jeśli zasadniczo dołączasz obiekt, a następnie ustawiasz stan na Zmodyfikowany, wówczas pierwotne wartości zostaną utracone. Aby zachować pierwotne wartości, musisz albo EF śledzić encję od zapytania do zapisu, albo musisz śledzić oryginalne wartości we własnym kodzie. –

+0

Przepraszamy - próbowałem opublikować kod w komentarzach, ale nie działał dobrze.Używam działania [HttpPost] kontrolera MVC. To wywołuje metodę "SaveProduct" w moim repozytorium produktu. W repozytorium wygląda na to, że rzeczywiście nazywam "context.Entry (product) .State = EntityState.Modified', a następnie I SaveChanges na kontekście. Czy byłbyś w stanie wskazać mi jakieś zasoby, które demonstrują dwie wspomniane techniki? A przynajmniej daj mi kilka wskazówek? –

+0

Ithink_ w MVC zalecany sposób to zrobić z ukrytych pól. Zasadniczo zapisałeś pierwotne wartości, które cię interesują, w ukrytych polach, a następnie przeczytałeś je później w swoim POST. Nie znam najlepszych praktyk w tym zakresie lub jeśli istnieje abstrakcja MVC, aby pomóc. –

Odpowiedz

11

Gdy EF pobiera obiekt z bazy danych, wykonuje migawkę oryginalnych wartości dla wszystkich właściwości tego obiektu. Później, w miarę wprowadzania zmian wartości tych właściwości, oryginalne wartości pozostaną niezmienione, podczas gdy bieżące wartości ulegną zmianie.

Jednak, aby tak się stało, EF musi śledzić podmiot w trakcie całego procesu. W sieci lub innej aplikacji warstwy n, zazwyczaj wartości są wysyłane do klienta, a kontekst używany do wysyłania zapytań do jednostki jest usuwany. Oznacza to, że jednostka nie jest już śledzona przez EF. To jest dobra i dobra praktyka.

Po wycofaniu aplikacji obiekt jest rekonstruowany przy użyciu wartości od klienta, a następnie ponownie dołączany do kontekstu i ustawiony w stan Zmodyfikowany. Jednak domyślnie jedynymi wartościami, które wracają od klienta, są bieżące wartości. Oryginalne wartości zostaną utracone. Zwykle nie ma to znaczenia, chyba że robisz optymistyczne współbieżności lub chcesz być bardzo ostrożny, tylko aktualizując wartości, które naprawdę się zmieniły. W takich przypadkach oryginalne wartości powinny być również wysyłane do klienta (zwykle jako ukryte pola w aplikacji internetowej), a następnie ponownie stosowane jako oryginalne wartości jako część procesu dołączania. Nie miało to miejsca w powyższym przykładzie i dlatego oryginalne wartości nie były wyświetlane zgodnie z oczekiwaniami.

+0

Jeszcze raz dziękuję za pomoc. Mam to teraz działa zgodnie z oczekiwaniami ... myślę. Tylko dla wyjaśnienia: Czy mówisz, że wywołanie ** context.Entry (product) .State = EntityState.Modified ** ponownie dołącza "produkt" do kontekstu, a zatem traci on ślad z jego OriginalValues? Również [tutaj] (http://stackoverflow.com/questions/9591165/ef-4-how-to-properly-update-object-in-dbcontext-using-mvc-with-repository-patte) jest nowym postem Zrobiłem dla kontynuacji (tylko dla odniesienia dla innych). –

+0

Ale dlaczego podejście działa w przykładzie śledzenia zmian powiązanym z pytaniem @JoeDePung? – Jensen

13

Jeżeli zmienisz

dbEntry.OriginalValues.GetValue<object>(propertyName); 

do

dbEntry.GetDatabaseValues().GetValue<object>(propertyName); 

wówczas, że pracuje.

+4

Działa, ale prawdopodobnie bardzo drogie do celów audytu. – eoleary

+1

Dlaczego bardzo ekspansywne? Jeśli chcesz poznać wartość przed i po zmianie, musisz wysłać zapytanie do bazy danych, nie ma mowy. – sintetico82

+0

to działa .. ale dlaczego nie ma oryginalnej wartości i dlaczego tak samo jak currentvalue @eoleary – Abi