Отменить изменения в сущностях каркаса сущностей - PullRequest
102 голосов
/ 29 марта 2011

это может быть тривиальный вопрос, но: поскольку структура сущностей ADO.NET автоматически отслеживает изменения (в сгенерированных сущностях) и, следовательно, сохраняет исходные значения, как я могу откатить изменения, внесенные в объекты сущностей?

У меня есть форма, которая позволяет пользователю редактировать набор сущностей «Клиент» в виде сетки.

Теперь у меня есть две кнопки «Принять» и «Вернуть»: если нажать «Принять», я вызываю Context.SaveChanges() и измененные объекты записываются обратно в базу данных. Если нажать «Вернуть», я бы хотел, чтобы все объекты получили свои исходные значения свойств. Какой будет код для этого?

Спасибо

Ответы [ 12 ]

136 голосов
/ 31 июля 2013

Запрос ChangeTracker из DbContext для грязных предметов.Установить состояние удаленных элементов в неизменном виде и добавленных элементов в отдельности.Для измененных элементов используйте исходные значения и установите текущие значения записи.Окончательно установите состояние измененной записи на неизменное:

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;
        }
    }
 }
66 голосов
/ 29 марта 2011

Операция возврата или отмены изменений в EF отсутствует.Каждая сущность имеет ObjectStateEntry в ObjectStateManager.Запись состояния содержит исходные и фактические значения, поэтому вы можете использовать исходные значения для перезаписи текущих значений, но вы должны сделать это вручную для каждого объекта.Он не будет воспринимать изменения в свойствах / отношениях навигации.

Распространенным способом «отмены изменений» является удаление контекста и перезагрузка сущностей.Если вы хотите избежать перезагрузки, вы должны создать клоны сущностей и изменить эти клоны в новом контексте объекта.Если пользователь отменит изменения, у вас останутся оригинальные объекты.

29 голосов
/ 23 ноября 2012
dbContext.Entry(entity).Reload();

Согласно MSDN :

Перезагружает объект из базы данных, перезаписывая любые значения свойств значениями из базы данных.После вызова этого метода сущность будет в состоянии Неизменено.

Обратите внимание, что возврат через запрос к базе данных имеет некоторые недостатки:

  • сетевой трафик
  • Перегрузка БД
  • увеличенное время отклика приложения
17 голосов
/ 09 декабря 2011

Это сработало для меня:

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

Где item - объект клиента, которого необходимо вернуть.

12 голосов
/ 01 августа 2013

Простой способ без отслеживания каких-либо изменений.Это должно быть быстрее, чем смотреть на все сущности.

public void Rollback()
{
    dataContext.Dispose();
    dataContext= new MyEntities(yourConnection);
}
6 голосов
/ 10 марта 2014
// 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; 
    } 
} 

Это сработало для меня. Однако вы должны перезагрузить ваши данные из контекста, чтобы перенести старые данные. Источник здесь

3 голосов
/ 05 мая 2012

"Это сработало для меня:

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

Где item - объект клиента, который необходимо вернуть. "


Я сделал тесты с ObjectContext.Refresh в SQL Azure, и «RefreshMode.StoreWins» запускает запрос к базе данных для каждого объекта и вызывает утечку производительности. На основании документации Microsoft ():

ClientWins: изменения свойств, внесенные в объекты в контексте объекта, не заменяются значениями из источника данных. При следующем вызове SaveChanges эти изменения отправляются в источник данных.

StoreWins: изменения свойств, внесенные в объекты в контексте объекта, заменяются значениями из источника данных.

ClientWins также не является хорошей идеей, потому что запуск .SaveChanges будет фиксировать отклоненные изменения в источнике данных.

Я пока не знаю, каков наилучший способ, потому что удаление контекста и создание нового вызывает исключение с сообщением: «Основной поставщик не удалось открыть», когда я пытаюсь выполнить любой запрос в созданном новом контексте.

С уважением,

Энрике Клаусинг

2 голосов
/ 24 августа 2013

Это пример того, о чем говорит Мрнка. Следующий метод перезаписывает текущие значения сущности исходными значениями, и не не вызывает базу данных. Мы делаем это, используя свойство OriginalValues ​​объекта DbEntityEntry, и используем отражение для установки значений общим способом. (Это работает с 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 голосов
/ 08 августа 2012

Я обнаружил, что это нормально работает в моем контексте:

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

2 голосов
/ 07 сентября 2011

Что касается меня, лучший способ сделать это - установить EntityState.Unchanged для каждой сущности, на которой вы хотите отменить изменения.Это гарантирует, что изменения отменены на FK и имеет немного более четкий синтаксис.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...