【发布时间】:2008-11-03 16:41:14
【问题描述】:
在 Linq to SQL 的 DataContext 上,我可以调用 SubmitChanges() 来提交所有更改。
我想要的是以某种方式拒绝数据上下文中的所有更改并回滚所有更改(最好不进入数据库)。
这可能吗?
【问题讨论】:
标签: linq linq-to-sql
在 Linq to SQL 的 DataContext 上,我可以调用 SubmitChanges() 来提交所有更改。
我想要的是以某种方式拒绝数据上下文中的所有更改并回滚所有更改(最好不进入数据库)。
这可能吗?
【问题讨论】:
标签: linq linq-to-sql
为什么不丢弃数据上下文并简单地将其替换为新实例?
【讨论】:
public static class DataContextExtensions
{
/// <summary>
/// Discard all pending changes of current DataContext.
/// All un-submitted changes, including insert/delete/modify will lost.
/// </summary>
/// <param name="context"></param>
public static void DiscardPendingChanges(this DataContext context)
{
context.RefreshPendingChanges(RefreshMode.OverwriteCurrentValues);
ChangeSet changeSet = context.GetChangeSet();
if (changeSet != null)
{
//Undo inserts
foreach (object objToInsert in changeSet.Inserts)
{
context.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
//Undo deletes
foreach (object objToDelete in changeSet.Deletes)
{
context.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
}
}
/// <summary>
/// Refreshes all pending Delete/Update entity objects of current DataContext according to the specified mode.
/// Nothing will do on Pending Insert entity objects.
/// </summary>
/// <param name="context"></param>
/// <param name="refreshMode">A value that specifies how optimistic concurrency conflicts are handled.</param>
public static void RefreshPendingChanges(this DataContext context, RefreshMode refreshMode)
{
ChangeSet changeSet = context.GetChangeSet();
if (changeSet != null)
{
context.Refresh(refreshMode, changeSet.Deletes);
context.Refresh(refreshMode, changeSet.Updates);
}
}
}
【讨论】:
在 .net 3.0 中,db.GetChangeSet().Updates.Clear() 用于更新,db.GetChangeSet().Inserts.Clear() 用于新项目,db.GetChangeSet().Deletes.Clear() 用于已删除项目。
在 .net 3.5 及更高版本中,GetChangeSet() 的结果现在是只读的,在 for 或 foreach 中循环集合并刷新每个 ChangeSet 表,就像 macias 在他的评论中所写的那样。
【讨论】:
正如 Haacked 所说,只需删除数据上下文。
您可能不应该长时间保持数据上下文处于活动状态。它们旨在以事务方式使用(即每个原子工作单元一个数据上下文)。如果您长时间保持数据上下文处于活动状态,则在更新陈旧实体时产生并发异常的风险更大。
【讨论】:
对 Updates、Delete 和 Inserts 集合调用 Clear() 不起作用。
GetOriginalEntityState() 可能很有用,但它只提供外键关系的 ID,而不是实际实体,因此您会得到一个分离的对象。
这里有一篇文章解释了如何从数据上下文中丢弃更改:http://graemehill.ca/discard-changes-in-linq-to-sql-datacontext
编辑:调用 Refresh() 将撤消更新,但不会删除和插入。
【讨论】:
刷新将起作用,但是您必须提供要重置的实体。
例如
dataContext.Refresh(RefreshMode.OverwriteCurrentValues, someObject);
【讨论】:
您可以使用 GetOriginalEntityState(..) 来获取对象的原始值,例如使用旧缓存值的客户。
您还可以遍历更改,例如仅更新和刷新特定对象而不是整个表,因为性能损失会很高。
foreach (Customer c in MyDBContext.GetChangeSet().Updates)
{
MyDBContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, c);
}
这将使用数据库中的持久数据恢复更改。
另一种解决方案是使用 Dispose() 转储您使用的数据上下文。
在任何情况下,覆盖例如集合中的 Insert 和 Remove 方法都是一个好习惯。您使用和添加的客户,例如一个 InsertOnSubmit() 调用。这将解决您的待处理插入和删除问题。
【讨论】:
我的应用程序是带有图标的 Outlook 样式,用于选择活动表单 (ListBox)。在允许用户更改其上下文之前,他们必须接受更改或丢弃它们。
var changes = db.GetChangeSet();
if ((changes.Updates.Count > 0) || (changes.Inserts.Count > 0) || (changes.Deletes.Count > 0))
{
if (MessageBox.Show("Would you like to save changes?", "Save Changes", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
db.SubmitChanges();
} else
{
//Rollback Changes
foreach (object objToInsert in changes.Inserts)
{
db.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
foreach (object objToDelete in changes.Deletes)
{
db.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
foreach (object objToUpdate in changes.Updates)
{
db.Refresh(RefreshMode.OverwriteCurrentValues, objToUpdate);
}
CurrentForm.SetObject(null); //Application Code to Clear active form
RefreshList(); //Application Code to Refresh active list
}
}
【讨论】:
在here 上写得很好,但这里是所用代码的复制和粘贴。
Public Sub DiscardInsertsAndDeletes(ByVal data As DataContext)
' Get the changes
Dim changes = data.GetChangeSet()
' Delete the insertions
For Each insertion In changes.Inserts
data.GetTable(insertion.GetType).DeleteOnSubmit(insertion)
Next
' Insert the deletions
For Each deletion In changes.Deletes
data.GetTable(deletion.GetType).InsertOnSubmit(deletion)
Next
End Sub
Public Sub DiscardUpdates(ByVal data As DataContext)
' Get the changes
Dim changes = data.GetChangeSet()
' Refresh the tables with updates
Dim updatedTables As New List(Of ITable)
For Each update In changes.Updates
Dim tbl = data.GetTable(update.GetType)
' Make sure not to refresh the same table twice
If updatedTables.Contains(tbl) Then
Continue For
Else
updatedTables.Add(tbl)
data.Refresh(RefreshMode.OverwriteCurrentValues, tbl)
End If
Next
End Sub
【讨论】:
这就是我的做法。我只是按照上面 Teddy 的例子进行了简化。不过,我有一个问题,为什么还要费心在 DELETES 上刷新?
public static bool UndoPendingChanges(this NtsSuiteDataContext dbContext)
{
if (dbContext.ChangesPending())
{
ChangeSet dbChangeSet = dbContext.GetChangeSet();
dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Deletes);
dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Updates);
//Undo Inserts
foreach (object objToInsert in dbChangeSet.Inserts)
{
dbContext.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
//Undo deletes
foreach (object objToDelete in dbChangeSet.Deletes)
{
dbContext.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
}
return true;
}
【讨论】: