.net 撤消实体框架实体中的更改
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5466677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Undo changes in entity framework entities
提问by MartinStettner
this might be a trivial question but: Since ADO.NET entity framework automatically tracks changes (in generated entities) and therefore keeps the original values, how can I rollback changes made to the entity objects?
这可能是一个微不足道的问题,但是:由于 ADO.NET 实体框架自动跟踪更改(在生成的实体中)并因此保留原始值,我如何回滚对实体对象所做的更改?
I have a form which allows the user to edit a set of "Customer" entities in a grid view.
我有一个表单,它允许用户在网格视图中编辑一组“客户”实体。
Now I have two buttons "Accept" and "Revert": if "Accept" is clicked, I call Context.SaveChanges()and the changed objects are written back to the database. If "Revert" is clicked, I would like for all objects to get their original property values. What would be the code for that?
现在我有两个按钮“接受”和“还原”:如果单击“接受”,我会调用Context.SaveChanges()并将更改的对象写回数据库。如果单击“还原”,我希望所有对象都获得其原始属性值。什么是代码?
Thanks
谢谢
采纳答案by Ladislav Mrnka
There is no revert or cancel changes operation in EF. Each entity has ObjectStateEntryin ObjectStateManager. State entry contains original and actual values so you can use original values to overwrite current values but you must do it manually for each entity. It will not reveret changes in navigation properties / relations.
EF 中没有还原或取消更改操作。每个实体都有ObjectStateEntry在ObjectStateManager. 状态条目包含原始值和实际值,因此您可以使用原始值覆盖当前值,但您必须为每个实体手动执行此操作。它不会恢复导航属性/关系的更改。
The common way to "revert changes" is disposing context and reload entities. If you want to avoid reloading you must create clones of entities and modify those clones in new object context. If user cancel changes you will still have original entities.
“还原更改”的常用方法是处理上下文并重新加载实体。如果您想避免重新加载,您必须创建实体的克隆并在新的对象上下文中修改这些克隆。如果用户取消更改,您仍将拥有原始实体。
回答by Taher Rahgooy
Query ChangeTracker of DbContext for dirty items. Set deleted items state to unchanged and added items to detached. For modified items, use original values and set current values of the entry. Finally set state of modified entry to unchanged:
查询 DbContext 的 ChangeTracker 以获取脏项。将已删除的项目状态设置为未更改,将添加的项目设置为已分离。对于修改项,使用原始值并设置条目的当前值。最后将修改条目的状态设置为未更改:
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;
}
}
}
回答by Lu55
dbContext.Entry(entity).Reload();
Accroding to MSDN:
根据MSDN:
Reloads the entity from the database overwriting any property values with values from the database. The entity will be in the Unchanged state after calling this method.
从数据库重新加载实体,用数据库中的值覆盖任何属性值。调用此方法后,实体将处于 Unchanged 状态。
Note that reverting through the request to database has some drawbacks:
请注意,通过请求恢复到数据库有一些缺点:
- network traffic
- DB overload
- the increased application response time
- 网络流量
- 数据库过载
- 增加的应用程序响应时间
回答by Matyas
This worked for me:
这对我有用:
dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);
Where itemis the customer entity to be reverted.
item要还原的客户实体在哪里。
回答by Guish
Easy way without tracking any changes. It should be faster than looking at every entities.
无需跟踪任何更改的简单方法。它应该比查看每个实体更快。
public void Rollback()
{
dataContext.Dispose();
dataContext= new MyEntities(yourConnection);
}
回答by Alejandro del Río
// 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 to reload your data from the context to bring the old data. Source here
它对我有用。但是,您必须从上下文重新加载数据以获取旧数据。来源在这里
回答by Henrique Clausing Cunha
"This worked for me:
“这对我有用:
dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);
Where itemis the customer entity to be reverted."
item要恢复的客户实体在哪里。”
I have made tests with ObjectContext.Refresh in SQL Azure, and the "RefreshMode.StoreWins" fires a query against database for each entity and causes a performance leak. Based on microsoft documentation ():
我已经在 SQL Azure 中使用 ObjectContext.Refresh 进行了测试,并且“RefreshMode.StoreWins”针对每个实体的数据库触发查询并导致性能泄漏。基于微软文档():
ClientWins : Property changes made to objects in the object context are not replaced with values from the data source. On the next call to SaveChanges, these changes are sent to the data source.
ClientWins :对对象上下文中的对象所做的属性更改不会替换为来自数据源的值。在下次调用 SaveChanges 时,这些更改将发送到数据源。
StoreWins : Property changes made to objects in the object context are replaced with values from the data source.
StoreWins :对对象上下文中的对象所做的属性更改将替换为来自数据源的值。
ClientWins isn't a good ideia neither, because firing .SaveChanges will commit "discarded" changes to the datasource.
ClientWins 也不是一个好主意,因为触发 .SaveChanges 会将“丢弃的”更改提交到数据源。
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 underlying provider failed on open" when I try to run any query on a new context created.
我不知道什么是最好的方法,因为当我尝试在创建的新上下文上运行任何查询时,处理上下文并创建新上下文会导致异常消息:“底层提供程序在打开时失败”。
regards,
问候,
Henrique Clausing
恩里克·克劳斯
回答by Alex Pop
I found this to be working fine in my context:
我发现这在我的上下文中工作正常:
Context.ObjectStateManager.ChangeObjectState(customer, EntityState.Unchanged);
Context.ObjectStateManager.ChangeObjectState(customer, EntityState.Unchanged);
回答by Naz
As for me, better method to do it is to set EntityState.Unchangedon every entity you want to undo changes on. This assures changes are reverted on FK and has a bit more clear syntax.
至于我,更好的方法是EntityState.Unchanged在每个要撤消更改的实体上进行设置。这可确保在 FK 上恢复更改并且具有更清晰的语法。
回答by BizarroDavid
This is an example of what Mrnka is talking about. The following method overwrites an entity's current values with the original values and doesn'tcall out the database. We do this by making use of the OriginalValues property of DbEntityEntry, and make use of reflection to set values in a generic way. (This works as of EntityFramework 5.0)
这是 Mrnka 所说的一个例子。以下方法使用原始值覆盖实体的当前值,并且不会调用数据库。我们通过使用 DbEntityEntry 的 OriginalValues 属性来做到这一点,并使用反射以通用方式设置值。(这适用于 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]);
}
}
}

