Answers:
EF中没有还原或取消更改操作。每个实体都有ObjectStateEntry
在ObjectStateManager
。状态条目包含原始值和实际值,因此您可以使用原始值覆盖当前值,但是必须对每个实体手动进行操作。它不会更改导航属性/关系的更改。
“还原更改”的常见方法是处理上下文并重新加载实体。如果要避免重新加载,则必须创建实体的克隆并在新的对象上下文中修改这些克隆。如果用户取消更改,您仍将拥有原始实体。
查询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;
}
}
}
State
为EntityState.Unchanged也会覆盖所有值,Original Values
因此无需调用SetValues
方法。
简单的方法,无需跟踪任何更改。它应该比查看每个实体都要快。
public void Rollback()
{
dataContext.Dispose();
dataContext= new MyEntities(yourConnection);
}
Rollback
过程要快一些,如果要还原整个数据库状态,这将是更好的选择。回滚可能会带来麻烦。
50ms+0*n= 50ms
。O(n)表示性能受对象数量的影响……性能可能2ms+0.5ms*n
……因此,在96个以下的对象中,它会更快,但时间会随着数据量的增加而线性增加。
// 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;
}
}
它为我工作。但是,您必须从上下文重新加载数据以带来旧数据。来源在这里
“这对我有用:
dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);
item
要还原的客户实体在哪里。”
我已经使用SQL Azure中的ObjectContext.Refresh进行了测试,并且“ RefreshMode.StoreWins”针对每个实体针对数据库触发查询,并导致性能泄漏。基于Microsoft文档():
ClientWins:对象上下文中对对象所做的属性更改不会替换为数据源中的值。在下一次调用SaveChanges时,这些更改将发送到数据源。
StoreWins:对象上下文中对对象所做的属性更改将替换为数据源中的值。
ClientWins也不是一个好主意,因为触发.SaveChanges会将“丢弃的”更改提交到数据源。
我不知道什么是最好的方法,因为当我尝试在创建的新上下文上运行任何查询时,处理上下文并创建新的上下文会导致消息异常:“底层提供程序在打开时失败”。
问候,
亨里克·克劳斯
对于我来说,更好的方法是EntityState.Unchanged
在要撤消更改的每个实体上进行设置。这样可以确保更改可以在FK上还原,并且语法更加清晰。
我发现这在我的情况下工作正常:
Context.ObjectStateManager.ChangeObjectState(customer, EntityState.Unchanged);
DbContext.SaveChanges()
,但不会将实体值恢复为原始值。并且,如果实体状态因后来的更改而被修改,那么保存时是否可能保留所有先前的修改?
这是Mrnka所说的一个例子。以下方法用原始值覆盖实体的当前值,并且不调出数据库。我们通过使用DbEntityEntry的OriginalValues属性来完成此操作,并利用反射以通用方式设置值。(从EntityFramework 5.0开始有效)
/// <summary>
/// Undoes any pending updates
/// </summary>
public void UndoUpdates( DbContext dbContext )
{
//Get list of entities that are marked as modified
List<DbEntityEntry> modifiedEntityList =
dbContext.ChangeTracker.Entries().Where(x => x.State == EntityState.Modified).ToList();
foreach( DbEntityEntry entity in modifiedEntityList )
{
DbPropertyValues propertyValues = entity.OriginalValues;
foreach (String propertyName in propertyValues.PropertyNames)
{
//Replace current values with original values
PropertyInfo property = entity.Entity.GetType().GetProperty(propertyName);
property.SetValue(entity.Entity, propertyValues[propertyName]);
}
}
}
我们将EF 4与旧版对象上下文配合使用。上面的解决方案都没有直接为我解决这个问题-尽管从长远来看,它是通过向正确的方向推动我来解决的。
我们不能仅仅处置和重建上下文,因为我们在内存中徘徊的某些对象(该死的延迟加载!)仍然附加在上下文中,但是有一些子对象尚未被加载。对于这些情况,我们需要将所有内容恢复到原始值,而不会影响数据库并且不删除现有连接。
以下是我们针对同一问题的解决方案:
public static void UndoAllChanges(OurEntities ctx)
{
foreach (ObjectStateEntry entry in
ctx.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached))
{
if (entry.State != EntityState.Unchanged)
{
ctx.Refresh(RefreshMode.StoreWins, entry.Entity);
}
}
}
我希望这对其他人有帮助。
上面的一些好主意,我选择实现ICloneable,然后选择一种简单的扩展方法。
在这里找到:如何在C#中克隆通用列表?
用作:
ReceiptHandler.ApplyDiscountToAllItemsOnReciept(LocalProductsOnReciept.Clone(), selectedDisc);
这样,我可以克隆我的产品实体列表,对每个商品应用折扣,而不必担心还原原始实体上的任何更改。无需与DBContext交谈并要求刷新或使用ChangeTracker。您可能会说我没有充分利用EF6,但这是一个非常好的简单的实现,并且避免了数据库崩溃。我不能说这是否对性能有影响。
Context.Refresh()
是您声称没有还原操作的反例吗?Refresh()
与处理上下文并丢失所有跟踪的更改相比,使用似乎是一种更好的方法(即,更容易针对特定实体)。