撤消实体框架实体中的更改

Mar*_*ner 110 .net entity-framework rollback

这可能是一个微不足道的问题但是:由于ADO.NET实体框架会自动跟踪更改(在生成的实体中)并因此保留原始值,我如何回滚对实体对象所做的更改?

我有一个表单,允许用户在网格视图中编辑一组"客户"实体.

现在我有两个按钮"Accept"和"Revert":如果单击"Accept",我会调用Context.SaveChanges()并将更改的对象写回数据库.如果单击"恢复",我希望所有对象获取其原始属性值.那个代码是什么?

谢谢

Tah*_*ooy 146

查询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;
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

  • 将`State`设置为**EntityState.Unchanged**将覆盖所有带有'Original Values`的值,因此不需要调用`SetValues`方法. (21认同)
  • 这个答案的清洁版本:http://stackoverflow.com/a/22098063/2498426 (10认同)
  • 您应该将原始值设置为已删除的条目.您可能先更改项目并在此之后将其删除. (5认同)
  • 谢谢-这真的帮助了我! (3认同)

Lad*_*nka 69

EF中没有恢复或取消更改操作.每个实体都有ObjectStateEntryObjectStateManager.状态条目包含原始值和实际值,因此您可以使用原始值覆盖当前值,但必须手动为每个实体执行此操作.它不会反映导航属性/关系的变化.

"还原更改"的常用方法是处理上下文和重新加载实体.如果要避免重新加载,则必须创建实体克隆并在新对象上下文中修改这些克隆.如果用户取消更改,您仍将拥有原始实体.

  • @robjb:否.刷新只能刷新您手动定义的单个实体或实体集合,但刷新功能仅影响简单属性(不是关系).它也没有解决添加或删除实体的问题. (14认同)
  • @LadislavMrnka当然`Context.Refresh()`是你声称没有还原操作的反例?使用`Refresh()`似乎是一种更好的方法(即更容易针对特定实体),而不是处理上下文并丢失所有跟踪的更改. (4认同)
  • 谢谢.在我看来,这似乎是一些通用解决方案的候选者,但...... (2认同)

Lu5*_*u55 30

dbContext.Entry(entity).Reload();
Run Code Online (Sandbox Code Playgroud)

加密到MSDN:

从数据库重新加载实体,使用数据库中的值覆盖任何属性值.调用此方法后,实体将处于Unchanged状态.

请注意,将请求还原到数据库有一些缺点:

  • 网络流量
  • DB过载
  • 应用程序响应时间增加


小智 17

这对我有用:

dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);
Run Code Online (Sandbox Code Playgroud)

item要还原的客户实体在哪里.


sal*_*uce 13

虽然这个问题早于 Entity Framework Core,但 EF Core 开发人员已经为这个问题提供了一个简单的解决方案。

从 EF Core 5.0 开始,ChangeTracker现在提供了一种清除跟踪实体的方法,该方法比分离所有更改的实体更有效。

context.ChangeTracker.Clear()
Run Code Online (Sandbox Code Playgroud)

停止跟踪所有当前跟踪的实体。

DbContext 被设计为具有较短的生命周期,其中为每个工作单元创建一个新实例。这种方式意味着当上下文在每个工作单元结束时被处理时,所有跟踪的实体都将被丢弃。但是,在创建新上下文实例不切实际的情况下,使用此方法清除所有跟踪的实体可能会很有用。

这种方法应该始终优先于分离每个被跟踪的实体。分离实体是一个缓慢的过程,可能会产生副作用。此方法在从上下文中清除所有跟踪的实体方面更加有效。

请注意,此方法不会生成 StateChanged 事件,因为实体不是单独分离的。

微软文档

此时,您可以从 中检索原始值DbContext


Gui*_*ish 12

轻松的方式,无需跟踪任何变化 它应该比查看每个实体更快.

public void Rollback()
{
    dataContext.Dispose();
    dataContext= new MyEntities(yourConnection);
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*Río 6

// 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; 
    } 
} 
Run Code Online (Sandbox Code Playgroud)

它对我有用.但是,您必须从上下文重新加载数据以带来旧数据.来源于