撤消实体框架实体中的更改
这可能是一个微不足道的问题,但是:由于 ADO.NET 实体框架自动跟踪(生成的实体中)更改并因此保留原始值,因此如何回滚对实体对象所做的更改?
我有一个表单,允许用户在网格视图中编辑一组“客户”实体。
现在我有两个按钮“接受”和“恢复”:如果单击“接受”,我将调用 Context.SaveChanges() 并将更改的对象写回数据库。如果单击“恢复”,我希望所有对象都获得其原始属性值。代码是什么?
谢谢
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?
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?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
查询 DbContext 的 ChangeTracker 中是否有脏项目。将已删除项目的状态设置为未更改,并将添加的项目设置为已分离。对于修改的项目,使用原始值并设置条目的当前值。最后将修改条目的状态设置为未更改:
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:
EF 中没有恢复或取消更改操作。每个实体在
ObjectStateManager
中都有ObjectStateEntry
。状态条目包含原始值和实际值,因此您可以使用原始值覆盖当前值,但必须为每个实体手动执行此操作。它不会恢复导航属性/关系的更改。“恢复更改”的常见方法是处理上下文并重新加载实体。如果您想避免重新加载,则必须创建实体的克隆并在新的对象上下文中修改这些克隆。如果用户取消更改,您仍然拥有原始实体。
There is no revert or cancel changes operation in EF. Each entity has
ObjectStateEntry
inObjectStateManager
. 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.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.
根据 MSDN:
请注意,通过对数据库的请求进行恢复有一些缺点:
Accroding to MSDN:
Note that reverting through the request to database has some drawbacks:
这对我有用:
其中
item
是要恢复的客户实体。This worked for me:
Where
item
is the customer entity to be reverted.虽然这个问题早于 Entity Framework Core,但 EF Core 开发人员已经为这个问题提供了一个简单的解决方案。
从 EF Core 5.0 开始,
ChangeTracker
现在提供了一种清除跟踪实体的方法,该方法比分离所有更改的实体更有效。微软文档
此时,您可以从
DbContext
中检索原始值。While this question predates Entity Framework Core, the EF Core developers have provided a simple solution to this problem.
As of EF Core 5.0, the
ChangeTracker
now provides a method to clear out tracked entities which is more efficient than detaching all changed entities.Microsoft Documentation
At that point, you can retrieve the original values from the
DbContext
.简单的方法,无需跟踪任何更改。它应该比查看每个实体更快。
Easy way without tracking any changes. It should be faster than looking at every entities.
这对我有用。但是,您必须从上下文重新加载数据才能带来旧数据。来源此处
It worked for me. However you must to reload your data from the context to bring the old data. Source here
对我来说,更好的方法是在要撤消更改的每个实体上设置 EntityState.Unchanged 。这确保了更改可以在 FK 上恢复,并且语法更加清晰。
As for me, better method to do it is to set
EntityState.Unchanged
on every entity you want to undo changes on. This assures changes are reverted on FK and has a bit more clear syntax.“这对我有用:
其中
item
是要恢复的客户实体。”我在 SQL Azure 中使用 ObjectContext.Refresh 进行了测试,并且“RefreshMode.StoreWins”对每个实体的数据库触发查询并导致性能泄漏。基于 Microsoft 文档 ():
ClientWins:对对象上下文中的对象进行的属性更改不会替换为数据源中的值。下次调用 SaveChanges 时,这些更改将发送到数据源。
StoreWins:对对象上下文中的对象所做的属性更改将替换为数据源中的值。
ClientWins 不是'这也不是一个好主意,因为触发 .SaveChanges 会将“丢弃”的更改提交到数据源。
我还不知道什么是最好的方法,因为当我尝试在创建的新上下文上运行任何查询时,处理上下文并创建新上下文会导致异常,并显示消息:“底层提供程序打开失败”。
问候,
恩里克·克劳辛
"This worked for me:
Where
item
is the customer entity to be reverted."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 ():
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.
StoreWins : Property changes made to objects in the object context are replaced with values from the data source.
ClientWins isn't a good ideia neither, because firing .SaveChanges will commit "discarded" changes to the datasource.
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
我们使用 EF 4 和旧对象上下文。上述解决方案都没有直接为我解答这个问题——尽管从长远来看它确实回答了这个问题,将我推向了正确的方向。
我们不能只是处理和重建上下文,因为我们在内存中徘徊的一些对象(该死的延迟加载!!)仍然附加到上下文,但有尚未加载的子对象。对于这些情况,我们需要将所有内容恢复到原始值,而不会对数据库造成影响,也不删除现有的连接。
以下是我们对同一问题的解决方案:
我希望这对其他人有帮助。
We are using EF 4, with the Legacy Object context. None of the above solutions directly answered this for me -- although it DID answer it in the long run by pushing me in the right direction.
We can't just dispose and rebuild the context because some of the objects we have hanging around in memory (damn that lazy loading!!) are still attached to the context but have children that are yet-to-be-loaded. For these cases we need to bump everything back to original values without hammering the database and without dropping the existing connection.
Below is our solution to this same issue:
I hope this helps others.
我发现这在我的上下文中运行良好:
Context.ObjectStateManager.ChangeObjectState(customer, EntityState.Unchanged);
I found this to be working fine in my context:
Context.ObjectStateManager.ChangeObjectState(customer, EntityState.Unchanged);
这是 Mrnka 所说的一个例子。以下方法用原始值覆盖实体的当前值,并且不调用数据库。我们通过使用 DbEntityEntry 的 OriginalValues 属性来实现此目的,并利用反射以通用方式设置值。 (这适用于 EntityFramework 5.0)
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't call 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)
上面的一些好的想法,我选择实现ICloneable然后一个简单的扩展方法。
此处找到:如何在 C# 中克隆通用列表?< /a>
用作:
这样我就能够克隆我的产品实体列表,对每个项目应用折扣,而不必担心恢复原始实体上的任何更改。无需与 DBContext 交谈并请求刷新或使用 ChangeTracker。您可能会说我没有充分利用 EF6,但这是一个非常好的且简单的实现,并且避免了数据库命中。我不能说这是否会影响性能。
Some good ideas above, I chose to implement ICloneable and then a simple extension method.
Found here: How do I clone a generic list in C#?
To be used as:
This way I was able to clone my product entities list, apply a discount to each item and not have to worry about reverting any changes on the original entity. No need to talk with the DBContext and ask for a refresh or work with the ChangeTracker. You might say I am not making full use of EF6 but this is a very nice and simple implementation and avoids a DB hit. I cannot say whether or not this has a performance hit.