如何在不丢失导航属性的情况下回滚实体删除

时间:2012-12-19 17:18:53

标签: .net entity-framework code-first navigational-properties

我们正在使用Entity Framework Code First,当我们不想SaveChanges()时,我遇到了尝试回滚插入,更新和删除实体更改的问题。

具体来说,我有一个datagridview,我将其用作TableEditor,以对一些辅助表进行更改。 datagridview绑定到DbSet<TEntity>

我的回滚更新似乎工作正常,我只是将currentValues设置回其OriginalValues,并将状态更改为unchanged

当一个记录被插入到gridview中时(但没有保存更改),它永远不会出现在实体类中,我再也看不到了......所以我猜它不会进入dbSet,并且这不需要回滚吗?

但我的主要问题在于删除

根据我的理解,当记录被“删除”时(例如tableData.Remove(currentItem);),只有标记才能删除,直到调用SaveChanges。因此,如果我将状态从deleted更改回unchanged,那应该处理回滚,对吗?

嗯,记录确实再次显示,但记录的导航属性已经消失了! (即包含外键的列和与其他实体的必需关系)。这是为什么??!

这是我到目前为止所做的:

    public void RollbackChanges(DbEntityEntry entry)
    {
        if (entry.State == EntityState.Modified)
        {
            foreach (var propertyName in entry.OriginalValues.PropertyNames)
            {
                entry.CurrentValues[propertyName] = entry.OriginalValues[propertyName];
            }
            entry.State = EntityState.Unchanged;
        }
        else if (entry.State == EntityState.Deleted)
        {
            entry.State = EntityState.Unchanged;
        }
        else if ((entry.State == EntityState.Added) || (entry.State == EntityState.Detached))
        {
            MessageBox.Show("I don't think this ever happens?");
        }
    }

使用示例:

    foreach (var entity in db.CertificationDecisions)
                {
                    DbEntityEntry entry = db.Entry(entity );
                    if (entry.State != EntityState.Unchanged)
                    {
                        RollbackChanges(entry);
                    }
                }

为什么导航属性会从记录中消失的任何想法? (或者我可以做些什么来让他们回来?)

<小时/> 修改 @Chris关于使用Refresh

我正在使用DbContext,所以我用这一行替换了我的回滚方法:

((IObjectContextAdapter)db).ObjectContext.Refresh(RefreshMode.StoreWins, db.CertificationDecisions);

然而,这似乎没有重新加载上下文,因为记录仍然缺失...我使用Refresh错了吗?

这听起来像是我的问题的可能解决方案,但我仍然想知道为什么会删除导航属性?

2 个答案:

答案 0 :(得分:3)

  

为什么导航属性会从记录中消失的任何想法?

由于外键关系,很可能会删除导航属性。让我们参加以下课程:

public class Dog
{
  public guid ID { get; set; }
  public string Title { get; set; }
}

public class Person
{
  public guid ID { get; set; }

  public ICollection<Dog> FavoriteDogs { get; set; }
}

在数据库中,FavoriteDogs的集合是一个引用了Person和Dog的表,但是由导航属性自动隐藏。当 Parent 状态设置为Deleted时,EF会自动将引用实体标记为已删除。如果 Parent 实体设置为Unchanged,则实体框架不会将这些引用实体设置为Unchanged,因为这样做不需要参照完整性(仅适用于删除操作)。

  

或者我可以做些什么来让他们回来?

EF目前没有自动流程可以执行此操作。 I don't believe there is even a manual way to change reference entities。新的Entity Framework 5 does not expose any way to view or change the state of DbRefrenceEntry

也许尝试DbReferenceEntry。Reload

答案 1 :(得分:2)

这不是我的问题的真正答案,但是到目前为止似乎对我有用的替代方案(如果有人正在寻找这个问题的答案):

基本上,我创建了一个新的上下文,并将一个实体从新的上下文传递给我的表单。如果我取消表单,我只是处理上下文,因此不保存任何更改(不需要回滚)。如果我保存表单,那么我将保存新上下文中的更改,但之后我需要让原始上下文知道更改。

所以我使用.Refresh,它似乎成功找到了新实体,但没有删除已删除的实体。然后我遍历实体并将它们与数据库值进行比较。

如果数据库返回'null',我们知道该实体已在数据库中删除,需要从上下文中删除。 (但是,我无法在循环中删除它,因为尝试更改上下文的内容而枚举它会导致错误,因此我将条目添加到列表中以便在单独的循环中从上下文中删除。)

以下是代码:
(注意:我不保证这是一个很好的解决方案,但它似乎对我有用)

            using (FmlaManagementContext auxDb = new FmlaManagementContext())
            {
                AuxiliaryTableEditor<State> statesTableEditor = new AuxiliaryTableEditor<State>(auxDb.States);
                if (statesTableEditor.ShowDialog() != DialogResult.OK)
                {
                    auxDb.Dispose();
                }
                else
                {
                    auxDb.SaveChanges();
                    auxDb.Dispose();

                    //refresh to pick up any new
                    ((IObjectContextAdapter)db).ObjectContext.Refresh(RefreshMode.StoreWins, db.States);

                    //loop through to remove deleted
                    List<State> nulls = new List<State>();
                    foreach (State state in db.States.Local)
                    {
                        DbEntityEntry entry = db.Entry(state);
                        DbPropertyValues databaseValues = entry.GetDatabaseValues();
                        if (databaseValues == null)
                        {
                            nulls.Add(state); 
                        }
                    }
                    foreach (State state in nulls)
                    {
                        db.States.Remove(state);
                    }
                }
            }

如果我最终改变或发现问题,我会尽量记得回来更新...