2012-11-22 12 views
9

Używam EF5 i nie wiem, dlaczego jednostka ma "zmodyfikowany" stan po ustawieniu jedynej zmienionej wartości PropertyValue tego obiektu do pierwotnej wartości.Entity Framework 5 - Dlaczego stan jednostki "zmodyfikowano" po ustawieniu właściwości PropertyValue na wartość oryginalną

using (TestDbContext context = new TestDbContext()) 
     { 
      string name = context.Person.First().Name; 

      // count is 0 
      int count = context.ChangeTracker.Entries().Count(e => e.State == EntityState.Modified); 

      // Change Value 
      context.Person.First().Name = "Test"; 

      // count is 1 
      count = context.ChangeTracker.Entries().Count(e => e.State == EntityState.Modified); 

      // Revert Value 
      context.Person.First().Name = name; 


      context.ChangeTracker.DetectChanges(); 

      // count is 1 
      count = context.ChangeTracker.Entries().Count(e => e.State == EntityState.Modified); 
     } 

Dlaczego? . :(

Odpowiedz

15

Bo tylko Entity Framework śledzi jeśli dane został zmodyfikowany, jeśli nie jest to różni się od jej pierwotnego zawartość

Używamy metody ładne zresetować stan na niezmodyfikowanej, gdy jednostka jest bez zmian:

public static void CheckIfModified(EntityObject entity, ObjectContext context) 
    { 
     if (entity.EntityState == EntityState.Modified) 
     { 
      ObjectStateEntry state = context.ObjectStateManager.GetObjectStateEntry(entity); 
      DbDataRecord orig = state.OriginalValues; 
      CurrentValueRecord curr = state.CurrentValues; 

      bool changed = false; 
      for (int i = 0; i < orig.FieldCount && !changed; ++i) 
      { 
       object origValue = orig.GetValue(i); 
       object curValue = curr.GetValue(i); 
       if (!origValue.Equals(curValue) && (!(origValue is byte[]) || !((byte[])origValue).SequenceEqual((byte[])curValue))) 
       { 
        changed = true; 
       } 
      } 

      if (!changed) 
      { 
       state.ChangeState(EntityState.Unchanged); 
      } 
     } 
    } 

Należy pamiętać, że ta metoda jest przeznaczona dla EF 4.0, nie dla nowszych wersji z DbContext, ale nie ma problemu, aby przepisać ją na EF 4.1+, już to zrobiłem, ale nie mogę znaleźć Kod teraz.

+1

Gdy masz 'zmieniony = prawda;' możesz "przerwać" z pętli, aby nie marnować cykli. –

+0

Dobra uwaga, dziękuję. :) Dodam, że do naszej realizacji. – user1793714

+0

Trochę tajemniczy, aby wprowadzić go w stan pętli. :) Na pierwszy rzut oka tęskniłbym za tym. –

3

Thx za podpowiedź :)

Oto moje rozwiązanie EF5 (DbContext). Nazywam tę metodę dla każdego DbEnityEntry że dostanę od ChangeTracker.Entries()

private void CheckIfDifferent(DbEntityEntry entry) 
    { 
     if (entry.State != EntityState.Modified) 
      return; 

     if (entry.OriginalValues.PropertyNames.Any(propertyName => !entry.OriginalValues[propertyName].Equals(entry.CurrentValues[propertyName]))) 
      return; 

     (this.dbContext as IObjectContextAdapter).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity).ChangeState(EntityState.Unchanged); 
    } 
+1

Należy pamiętać, że twoja wersja jest wadliwa. Podczas porównywania tablic bajtów (obiektów typu blob, rowversion) nie trzeba używać metody .Equals(). Sprawdź ponownie moją wersję. A jeśli moja odpowiedź ci pomogła, możesz oznaczyć ją jako odpowiedź. :) Używaj również ((IObjectContextAdapter) this.dbContext) zamiast swojej wersji. To da ci niewielką optymalizację prędkości, ale co ważniejsze: Wyrzuci poprawny wyjątek, gdy dbContext nie jest IObjectContextAdapter, zamiast wyjątku NullReferenceException (mało prawdopodobne, ale raczej programowe defensywne). – user1793714

+0

@ user1793714 thx :) – user1481065

+1

Wierzę, że 'entry.OriginalValues.PropertyNames.Any (propertyName =>! Entry.OriginalValues ​​[propertyName] .Equals (entry.CurrentValues ​​[propertyName]))' spowoduje błąd, jeśli dowolna wartość OriginalValue jest zerowa . Powinien istnieć test dla OriginalValue ma wartość null, a CurrentValue nie ma wartości null, aby pokryć wyjątek. – SteveCinq

0

budynku na odpowiedź User1481065, w celu przezwyciężenia możliwość wyjątek, jeśli którykolwiek z OriginalValues jest null, spróbuj poniżej. Przyjąłem kontekst, który może zawierać więcej niż jeden podmiot, który może mieć aktualizacje (które niekoniecznie muszą być rzeczywistymi zmianami wartości).

_dirty = False 
Dim oChanges As IEnumerable(Of DbEntityEntry(Of CsSetting)) = _dbContext.ChangeTracker.Entries(Of CsSetting)().Where(Function(r) r.State <> EntityState.Unchanged) 
For Each c As DbEntityEntry(Of CsSetting) In oChanges 
    _dirty = c.OriginalValues.PropertyNames.Any(Function(n) (c.OriginalValues(n) Is Nothing And c.CurrentValues(n) IsNot Nothing) OrElse (c.OriginalValues(n) IsNot Nothing AndAlso Not c.OriginalValues(n).Equals(c.CurrentValues(n)))) 
    If _dirty Then Exit For 
Next c 
Return _dirty 

Może nie trzeba pętlę, a tym samym ustawienie wstępne o _dirty.

Powiązane problemy