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

sǝɯ*_*ɯɐſ 5 .net entity-framework code-first navigational-properties

我们正在使用实体框架代码优先,我遇到了在我们不希望的情况下尝试回滚插入,更新和删除实体更改的问题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?");
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法示例:

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

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


编辑: @Chris关于使用Refresh:

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

((IObjectContextAdapter)db).ObjectContext.Refresh(RefreshMode.StoreWins, db.CertificationDecisions);
Run Code Online (Sandbox Code Playgroud)

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

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

sǝɯ*_*ɯɐſ 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);
                    }
                }
            }
Run Code Online (Sandbox Code Playgroud)

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