Discard changes in entity entities

This may be a trivial question, but: Since the ADO.NET entity infrastructure automatically tracks changes (in generated entities) and therefore retains its original values, how can I undo changes made to entity objects?

I have a form that allows a user to edit a set of Customer objects in a grid.

Now I have two Accept and Cancel buttons: if I click Accept, I call Context.SaveChanges() , and the changed objects are written back to the database. If I click Revert, I would like all the objects to get their original property values. What will be the code for this?

thank

+99
entity-framework rollback
Mar 29 2018-11-11T00:
source share
13 answers

EF does not have a rollback or rollback operation. Each object has an ObjectStateEntry in an ObjectStateManager . The status record contains the original and actual values, so you can use the original values ​​to overwrite the current values, but you must do this manually for each object. He will not review changes to the properties / relationships of the navigation.

A common way to “revert changes” is to delete the context and reload objects. If you want to avoid reloading, you must create clones of the objects and modify these clones in the context of the new object. If the user cancels the changes, you will still have the original objects.

+64
Mar 29 2018-11-11T00:
source share

Query ChangeTracker from DbContext for dirty items. You can install deleted items without changes and add items to a separate one. For changed items, use the original values ​​and set the current values ​​of the record. Finally, change the state of the changed record to unchanged:

 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; } } } 
+133
Jul 31 '13 at 10:31 on
source share
 dbContext.Entry(entity).Reload(); 

Enabling MSDN :

Reloads the object from the database, overwriting any property values ​​with values ​​from the database. The entity will be unchanged after calling this method.

Please note that returning a database query has some disadvantages:

  • network traffic
  • DB overload
  • increased application response time
+27
Nov 22 '12 at 23:00
source share

This worked for me:

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

Where item is the customer item be returned.

+17
Dec 08 2018-11-11T00:
source share

A simple way without tracking changes. It should be faster than looking at all entities.

 public void Rollback() { dataContext.Dispose(); dataContext= new MyEntities(yourConnection); } 
+12
Jul 31 '13 at 20:56
source share
 // 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; } } 

It worked for me. However, you must reload your data from the context in order to fetch the old data. Source here

+6
Mar 10 '14 at 2:45
source share

"It worked for me:

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

Where item is the customer item be returned.




I ran tests with ObjectContext.Refresh in SQL Azure, and "RefreshMode.StoreWins" launches a database query for each object and causes a performance leak. Based on Microsoft documentation ():

ClientWins: Property changes made by objects in the context of an object are not replaced by values ​​from the data source. The next time SaveChanges is called, these changes are sent to the data source.

StoreWins: Property changes made to objects in the context of the object are replaced with values ​​from the data source.

ClientWins is not a good idea either because firing .SaveChanges will make "discarded" changes to the data source.

I don’t know what’s best, because getting rid of the context and creating a new one caused an exception from the message: “The original provider refused to open” when I try to run any request in the newly created context.

Respectfully,

Henrique clausing

+3
May 04 '12 at 21:17
source share

For me, the best way to do this is to set EntityState.Unchanged to every object that you want to cancel. This ensures that changes are returned to the FK and have clearer syntax.

+2
Sep 07 2018-11-11T00:
source share

I found this to work fine in my context:

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

+2
Aug 08 2018-12-12T00:
source share

This is an example of what Mnnka is talking about. The following method overwrites the current values ​​of the entity with the original values, and does not call the database. We do this using the OriginalValues ​​property of the DbEntityEntry object and use reflection to set values ​​in a general way. (This works with 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
Aug 23 '13 at 23:21
source share

Some good ideas above, I decided to implement ICloneable and then a simple extension method.

Found here: How to clone a generic list in C #?

Used as:

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

Thus, I was able to clone the list of my products, apply a discount to each item and not worry about returning any changes to the original object. No need to talk to DBContext and request updates or work with ChangeTracker. You can say that I do not fully use EF6, but this is a very good and simple implementation and avoids database hits. I can’t say if this has a performance hit.

0
Aug 17 '14 at 15:36
source share

We use EF 4 with the context of an obsolete object. None of the above solutions directly answered me, although in the end I answered it by pushing me in the right direction.

We cannot just manage and rearrange the context, because some of the objects that we hung in memory (hell, lazy loading!) Are still attached to the context, but have child objects that are not loaded yet. For these cases, we need to return all initial values ​​without clogging the database and not discarding the existing connection.

Below is our solution to the same problem:

  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); } } } 

I hope this helps others.

0
Oct 26 '16 at 10:19
source share
 context.TEntity.Local.Clear(); 
0
May 12 '19 at 20:36
source share



All Articles