Cofanie zmian w encjach ramowych

To może być trywialne pytanie, ale: ponieważ ADO.NET struktura encji automatycznie śledzi zmiany (w generowanych encjach) i dlatego zachowuje oryginalne wartości, jak mogę cofnąć zmiany wprowadzone w obiektach encji?

Mam formularz, który pozwala użytkownikowi edytować zestaw elementów "Klienta" w widoku siatki.

Teraz mam dwa przyciski "Accept " i" Revert": jeśli klikniesz "Accept", wywołuję Context.SaveChanges() i zmienione obiekty są zapisywane z powrotem do bazy danych. Jeśli "Revert" jest kliknij, chciałbym, aby wszystkie obiekty, aby uzyskać ich oryginalne wartości właściwości. Jaki byłby na to kod?

Thanks

Author: MartinStettner, 2011-03-29

12 answers

Nie ma operacji przywracania lub anulowania zmian w EF. Każda jednostka ma ObjectStateEntry W ObjectStateManager. Wpis State zawiera oryginalne i rzeczywiste wartości, więc możesz użyć oryginalnych wartości do nadpisania bieżących wartości, ale musisz to zrobić ręcznie dla każdego elementu. Nie przywróci zmian we właściwościach / relacjach nawigacyjnych.

Powszechnym sposobem na "odwrócenie zmian" jest usuwanie kontekstu i przeładowywanie encji. Jeśli chcesz uniknąć przeładowania, musisz utworzyć klony encji i zmodyfikować te klony w nowym kontekst obiektu. Jeśli Użytkownik anuluje zmiany, nadal będziesz mieć oryginalne elementy.

 61
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-29 06:09:31

Query ChangeTracker DbContext dla brudnych elementów. Ustaw stan usuniętych elementów na niezmieniony, a dodane elementy na odłączony. Dla zmodyfikowanych pozycji należy użyć wartości oryginalnych i ustawić bieżące wartości wpisu. Na koniec ustaw stan zmodyfikowanego wpisu na niezmieniony:

public void RollBack()
{
    var context = DataContextFactory.GetDataContext();
    var changedEntries = context.ChangeTracker.Entries()
        .Where(x => x.State != EntityState.Unchanged).ToList();

    foreach (var entry in changedEntries)
    {
        switch(entry.State)
        {
            case EntityState.Modified:
                entry.CurrentValues.SetValues(entry.OriginalValues);
                entry.State = EntityState.Unchanged;
                break;
            case EntityState.Added:
                entry.State = EntityState.Detached;
                break;
            case EntityState.Deleted:
                entry.State = EntityState.Unchanged;
                break;
        }
    }
 }
 122
Author: Taher Rahgooy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-23 04:08:05
dbContext.Entry(entity).Reload();

Do MSDN :

przeładowuje obiekt z bazy danych nadpisując dowolne wartości właściwości wartościami z bazy danych. Jednostka będzie w niezmienionej stan po wywołaniu tej metody.

Zwróć uwagę, że przywrócenie żądania do bazy danych ma pewne wady:

  • ruch sieciowy
  • DB overload
  • wydłużony czas reakcji aplikacji
 25
Author: Lu55,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-22 23:00:17

To mi pomogło:

dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);

Gdzie item jest podmiotem klienta, który ma zostać przywrócony.

 17
Author: Matyas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-09 00:53:55

Łatwy sposób bez śledzenia zmian. To powinno być szybsze niż patrzenie na wszystkie byty.

public void Rollback()
{
    dataContext.Dispose();
    dataContext= new MyEntities(yourConnection);
}
 11
Author: Guish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-31 20:56:45
// Undo the changes of all entries. 
foreach (DbEntityEntry entry in context.ChangeTracker.Entries()) 
{ 
    switch (entry.State) 
    { 
        // Under the covers, changing the state of an entity from  
        // Modified to Unchanged first sets the values of all  
        // properties to the original values that were read from  
        // the database when it was queried, and then marks the  
        // entity as Unchanged. This will also reject changes to  
        // FK relationships since the original value of the FK  
        // will be restored. 
        case EntityState.Modified: 
            entry.State = EntityState.Unchanged; 
            break; 
        case EntityState.Added: 
            entry.State = EntityState.Detached; 
            break; 
        // If the EntityState is the Deleted, reload the date from the database.   
        case EntityState.Deleted: 
            entry.Reload(); 
            break; 
        default: break; 
    } 
} 
U mnie zadziałało. Musisz jednak przeładować swoje dane z kontekstu, aby przynieść stare dane. Źródło Tutaj
 6
Author: Alejandro del Río,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-25 08:31:07

"to mi się udało:

dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);

Gdzie item jest podmiotem klienta, który ma zostać przywrócony."


Wykonałem testy z ObjectContext.Odśwież w SQL Azure i " RefreshMode.StoreWins " wywołuje zapytanie z bazą danych dla każdej jednostki i powoduje wyciek wydajności. Na podstawie dokumentacji microsoft ():

ClientWins: zmiany właściwości obiektów w kontekście obiektu nie są zastępowane wartościami ze źródła danych. Przy kolejnym wezwaniu do ratowania, te zmiany są wysyłane do źródła danych.

StoreWins : zmiany właściwości obiektów w kontekście obiektu zostaną zastąpione wartościami ze źródła danych.

ClientWins też nie jest dobrą ideą, boSaveChanges zatwierdzi "odrzucone" zmiany w źródle danych.

I dont 'know what' s the best way yet, because disposing the context and creating a new one is caused a exception with message: "The based provider failed on open" when I spróbuj uruchomić dowolne zapytanie w nowym utworzonym kontekście.

Pozdrawiam,

Henrique Clausing]}
 3
Author: Henrique Clausing Cunha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-04 21:17:03

Jak dla mnie, lepszą metodą jest ustawienie EntityState.Unchanged Na każdym obiekcie, na którym chcesz cofnąć zmiany. Zapewnia to, że zmiany są przywracane na FK i ma nieco bardziej przejrzystą składnię.

 2
Author: Nazar Gargol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-07 08:31:56

Okazało się, że to działa dobrze w moim kontekście:

Context.ObjectStateManager.ChangeObjectState(customer, EntityState.Unchanged);

 2
Author: Alex Pop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-08 15:23:45

To jest przykład tego, o czym mówi Mrnka. Poniższa metoda nadpisuje bieżące wartości encji wartościami oryginalnymi i nie wywołuje bazy danych. Robimy to, korzystając z właściwości OriginalValues DbEntityEntry i używamy reflection do ustawiania wartości w sposób ogólny. (Działa to od EntityFramework 5.0)

/// <summary>
/// Undoes any pending updates 
/// </summary>
public void UndoUpdates( DbContext dbContext )
{
    //Get list of entities that are marked as modified
    List<DbEntityEntry> modifiedEntityList = 
        dbContext.ChangeTracker.Entries().Where(x => x.State == EntityState.Modified).ToList();

    foreach(  DbEntityEntry entity in modifiedEntityList ) 
    {
        DbPropertyValues propertyValues = entity.OriginalValues;
        foreach (String propertyName in propertyValues.PropertyNames)
        {                    
            //Replace current values with original values
            PropertyInfo property = entity.Entity.GetType().GetProperty(propertyName);
            property.SetValue(entity.Entity, propertyValues[propertyName]); 
        }
    }
}
 2
Author: BizarroDavid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-23 23:30:57

Używamy EF 4, z kontekstem obiektów starszych. Żadne z powyższych rozwiązań nie odpowiedziało na to bezpośrednio za mnie-chociaż na dłuższą metę odpowiedziało popychając mnie we właściwym kierunku.

Nie możemy po prostu pozbywać się i odbudowywać kontekstu, ponieważ niektóre obiekty, które mamy w pamięci (cholera, leniwe Ładowanie!!) są nadal przywiązane do kontekstu, ale mają dzieci, które są jeszcze do załadowania. W takich przypadkach musimy wszystko przywrócić do pierwotnych wartości bez uruchamianie bazy danych bez utraty istniejącego połączenia.

Poniżej znajduje się nasze rozwiązanie tego samego problemu:

    public static void UndoAllChanges(OurEntities ctx)
    {
        foreach (ObjectStateEntry entry in
            ctx.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached))
        {
            if (entry.State != EntityState.Unchanged)
            {
                ctx.Refresh(RefreshMode.StoreWins, entry.Entity);
            }
        }
    }
Mam nadzieję, że to pomoże innym.
 1
Author: Jerry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-26 22:19:43

Kilka dobrych pomysłów powyżej, zdecydowałem się zaimplementować ICloneable, a następnie prostą metodę rozszerzenia.

Znalezione tutaj: Jak sklonować listę generyczną w C#?

Stosować jako:

ReceiptHandler.ApplyDiscountToAllItemsOnReciept(LocalProductsOnReciept.Clone(), selectedDisc);

W ten sposób udało mi się sklonować listę podmiotów produktu, zastosować rabat na każdy przedmiot i nie muszę się martwić o przywrócenie jakichkolwiek zmian na oryginalnym podmiocie. Nie ma potrzeby rozmawiać z DBContext i poprosić o odświeżenie lub pracę z ChangeTracker. Można powiedzieć, że nie wykorzystuję w pełni EF6 ale jest to bardzo ładna i prosta implementacja i unika trafienia DB. Nie mogę powiedzieć, czy to ma hit wydajności.

 0
Author: IbrarMumtaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:46