我们正在使用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
错了吗?
这听起来像是我的问题的可能解决方案,但我仍然想知道为什么会删除导航属性?
答案 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);
}
}
}
如果我最终改变或发现问题,我会尽量记得回来更新...