这有点夸张,我希望你会发现这个具有挑战性的问题和我一样有趣......:)
我有一个名为DataContext
的{{1}}子类,其中我使用以下格式的代码覆盖了MyDataContext
方法:
SubmitChanges()
到目前为止,这么好。但是,如果BeginTransaction(); // my own implementation
IList<object> Updates = GetChangeSet().Updates;
foreach (object obj in Updates) {
MyClass mc = obj as MyClass;
if (mc != null)
mc.BeforeUpdate(); // virtual method in MyClass to allow pre-save processing
}
// This is followed by similar code for the Deletes and Inserts, then:
base.SubmitChanges();
// Then do post-save processing...
foreach (object obj in Updates) {
MyClass mc = obj as MyClass;
if (mc != null)
mc.AfterUpdate(); // virtual method in MyClass to allow post-save processing
}
// similar code for Inserts and Deletes
// ...
CommitTransaction();
// obviously all enclosed in a try-catch block where the catch does a rollback
的实现在其MyClass
或SubmitChanges()
方法中调用BeforeUpdate()
,则会出现一些问题。现在我们有一个可能导致堆栈溢出的递归。
我想到解决这个问题的一种方法是在AfterUpdate()
的开头有一个递归阻塞变量。但是如果保存被阻止该怎么办?我无法将其转换为新线程;调用线程可能要求SubmitChanges()
调用是同步的,例如如果需要在保存后立即访问自动编号属性。
要考虑的另一个因素是,如果在预处理或后处理过程中更改了任何对象,我还需要他们的 SubmitChanges()
和BeforeSave()
方法被召唤。
是否有一些聪明的教科书方式可以整齐而正确地完成这项工作?
答案 0 :(得分:2)
我想到的一种解决方法 这是一个递归阻塞 变量在开头 的SubmitChanges()。但如果做什么该怎么办 保存被阻止了吗?
抛出 NotSupportedException 。你不应该支持在BeforeChanges上发生的另一个SubmitChanges ...正是你正在做的事情,允许在SubmitChanges被调用之前发生一些更改。
关于调用其BeforeUpdate的更新对象,您可以在原始列表中调用BeforeUpdate之后检查SubmitChanges之前是否有新的更新对象,并且直到没有额外更新的对象为止。
AfterUpdate也是如此,类似的事情是对内存中的对象进行更改...而不是将更多数据保存到数据库。
尝试在系统中的不同实体上添加SubmitChanges,必然会在系统中产生一些性能问题。
答案 1 :(得分:1)
我唯一的想法是在你工作的时候创造一种缓冲;存储您要保存的对象。
这样的事情:
class MyDataContext : DataContext
{
private bool _WorkingFlag = false; // indicates whether we're currently saving
private List<object> _UpdateBuffer = new List<object>();
// ... other buffers here
protected void BeginTransaction()
{
// implementation
}
protected void CommitTransaction()
{
// implementation
}
public override void SubmitChanges()
{
BeginTransaction();
IList<object> updates = GetChangeSet().Updates;
// also inserts and deletes
if (_WorkingFlag)
{
_UpdateBuffer.AddRange(updates);
// also inserts and deletes
return;
}
_WorkingFlag = true;
updates = updates.Concat(_UpdateBuffer).ToList(); // merge the updates with the buffer
foreach (object obj in updates) // do the stuff here...
{
MyClass mc = obj as MyClass;
if (mc != null)
mc.BeforeUpdate(); // virtual method in MyClass to allow pre-save processing
}
_UpdateBuffer.Clear(); // clear the buffer
// ... same for inserts and deletes ...
base.SubmitChanges();
// ... after submit, simply foreach ...
CommitTransaction();
_WorkingFlag = false;
// of course all in try... catch, make sure _WorkingFlag is set back to false in the finally block
}
}
我希望它能正常工作,我没有测试过。
答案 2 :(得分:0)
现在已经给出了答案信用,这是我实际实施解决方案的方式:
private bool _Busy = false;
public override void SubmitChanges(ConflictMode failureMode) {
if (_Busy)
return; // no action & no error; just let this SubmitChanges handle all nested submissions.
try {
_Busy = true;
BeginTransaction();
Dictionary<MyClass, bool> myUpdates = new Dictionary<MyClass, bool>();
Dictionary<MyClass, bool> myInserts = new Dictionary<MyClass, bool>();
Dictionary<MyClass, bool> myDeletes = new Dictionary<MyClass, bool>();
SynchronizeChanges(myUpdates, GetChangeSet().Updates);
SynchronizeChanges(myInserts, GetChangeSet().Inserts);
SynchronizeChanges(myDeletes, GetChangeSet().Deletes);
while (myInserts.Any(i => i.Value == false) || myUpdates.Any(u => u.Value == false) || myDeletes.Any(d => d.Value == false)) {
List<MyClass> tmp = myInserts.Where(i => i.Value == false).Select(i => i.Key).ToList();
foreach (MyClass mc in tmp) {
mc.BeforeInsert();
myInserts[lt] = true;
}
tmp = myUpdates.Where(u => u.Value == false).Select(u => u.Key).ToList();
foreach (MyClass mc in tmp) {
mc.BeforeUpdate();
myInserts[lt] = true;
}
tmp = myDeletes.Where(d => d.Value == false).Select(d => d.Key).ToList();
foreach (MyClass mc in tmp) {
mc.BeforeDelete();
myInserts[lt] = true;
}
// before calling base.SubmitChanges(), make sure that nothing else got changed:
SynchronizeChanges(myUpdates, GetChangeSet().Updates);
SynchronizeChanges(myInserts, GetChangeSet().Inserts);
SynchronizeChanges(myDeletes, GetChangeSet().Deletes);
}
base.SubmitChanges(failureMode);
// now the After- methods
foreach (MyClass mc in mcInserts.Keys) {
mc.AfterInsert();
}
foreach (MyClass mc in mcUpdates.Keys) {
mc.AfterUpdate();
}
foreach (MyClass mc in mcDeletes.Keys) {
mc.AfterDelete();
}
CommitTransaction();
} catch {
RollbackTransaction();
throw;
} finally {
_Busy = false;
}
// now, just in case any of the After... functions triggered a change:
if (GetChangeSet().Deletes.Count + GetChangeSet().Inserts.Count + GetChangeSet().Updates.Count > 0)
SubmitChanges();
}
private void SynchronizeChanges(Dictionary<MyClass, bool> mcDict, IList<object> iList) {
var q = iList.OfType<MyClass>().Where(i => !mcDict.ContainsKey(i));
q.ToList().ForEach(i => mcDict[i] = false);
}