C# 在DataContext.SubmitChanges()期间防止递归

C# 在DataContext.SubmitChanges()期间防止递归,c#,linq,linq-to-sql,C#,Linq,Linq To Sql,这有点矫揉造作,我希望你会发现这个具有挑战性的问题和我一样有趣……:) 我有一个子类DataContext,名为MyDataContext,其中我用以下形式的代码重写了SubmitChanges()方法: BeginTransaction(); // my own implementation IList<object> Updates = GetChangeSet().Updates; foreach (object obj in Updates) { MyClass mc =

这有点矫揉造作,我希望你会发现这个具有挑战性的问题和我一样有趣……:)

我有一个子类
DataContext
,名为
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
BeginTransaction();//我自己的实现
IList Updates=GetChangeSet()。更新;
foreach(更新中的对象对象对象){
MyClass mc=作为MyClass的obj;
如果(mc!=null)
mc.BeforeUpdate();//MyClass中允许预保存处理的虚拟方法
}
//接下来是删除和插入的类似代码,然后:
base.SubmitChanges();
//然后进行保存后处理。。。
foreach(更新中的对象对象对象){
MyClass mc=作为MyClass的obj;
如果(mc!=null)
mc.AfterUpdate();//MyClass中允许保存后处理的虚拟方法
}
//插入和删除的类似代码
// ...
提交交易();
//显然,所有这些都包含在try-catch块中,catch在其中执行回滚
到目前为止,一切顺利。但是,如果
MyClass
的实现在其
BeforeUpdate()
AfterUpdate()方法中调用
SubmitChanges()
,就会出现一个小问题。现在我们有一个递归,它很容易导致堆栈溢出

解决这个问题的一种方法是在
SubmitChanges()
的开头有一个递归阻塞变量。但是,如果存储被阻止,该怎么办?我不能把它拆分成新的线程;调用线程可能要求
SubmitChanges()
调用是同步的,例如,如果它需要在保存后立即访问自动编号属性

另一个要考虑的因素是,如果在前处理或后处理过程中有任何对象被改变,我也希望其<代码>前()/<代码>和<代码>后备()> <代码>方法。< /P>


有没有什么巧妙的教科书方法能把这一切干净利落地做好?

我唯一的想法就是在你工作时创造一种缓冲;存储要保存的对象

大概是这样的:

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
   }
}
类MyDataContext:DataContext
{
private bool _WorkingFlag=false;//表示当前是否保存
私有列表_UpdateBuffer=新列表();
//…这里还有其他缓冲区
受保护的void BeginTransaction()
{
//实施
}
受保护的无效提交交易()
{
//实施
}
公共覆盖无效提交更改()
{
BeginTransaction();
IList updates=GetChangeSet()。更新;
//也可以插入和删除
如果(_工作标志)
{
_UpdateBuffer.AddRange(更新);
//也可以插入和删除
返回;
}
_WorkingFlag=true;
updates=updates.Concat(_UpdateBuffer).ToList();//将更新与缓冲区合并
foreach(更新中的对象obj)//在这里做这些事情。。。
{
MyClass mc=作为MyClass的obj;
如果(mc!=null)
mc.BeforeUpdate();//MyClass中允许预保存处理的虚拟方法
}
_UpdateBuffer.Clear();//清除缓冲区
//…对于插入和删除也是如此。。。
base.SubmitChanges();
//…提交后,只需提交。。。
提交交易();
_WorkingFlag=false;
//当然,在try…catch中,确保在finally块中将_WorkingFlag设置回false
}
}
我希望这会很好,我没有测试它

我想到的一种方法是 这是一个递归块 开始时的变量 SubmitChanges()。但是如果 保存被阻止了吗

抛出一个不支持的异常。您不应支持在BeforeChanges上发生的另一次提交更改。。。这正是您正在做的,允许在调用SubmitChanges之前进行一些更改

关于调用BeforeUpdate的更新对象,您可以在原始列表中调用BeforeUpdate之后,在提交更改之前检查是否有新的更新对象,直到没有额外的更新对象为止

这同样适用于AfterUpdate,类似于对内存中的对象进行更改。。。不将更多数据保存到数据库


尝试在系统中的不同实体上添加提交更改,必然会在系统中产生一些性能问题。

既然已经给出了答案,下面是我如何实际实施解决方案的:

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);
}
private bool\u Busy=false;
公共覆盖无效提交更改(冲突模式故障模式){
如果(忙)
return;//无操作&无错误;只需让此SubmitChanges处理所有嵌套提交即可。
试一试{
_忙=真;
BeginTransaction();
Dictionary myUpdates=新字典();
Dictionary myInserts=新字典();
字典myDeletes=新字典();
同步更改(myUpdates,GetChangeSet().Updates);
同步更改(myInserts,GetChangeSet().Inserts);
同步更改(myDeletes,GetChangeSet().Deletes);
while(myInserts.Any(i=>i.Value==false)| | myUpdates.Any(u=>u.Value==false)| | myDeletes.Any(d=>d.Value==false)){
列出tmp=myInserts.Where(i=>i.Value==false);
foreach(tmp中的MyClass mc){
mc.beforeist();
myInserts[lt]=真;
}
tmp=myUpdates.Where(u=>u.Value==false);
foreach(tmp中的MyClass mc){
mc.BeforeUpdate();
myInserts[lt]=真;
}
tmp=myDeletes.Where(d=>d.Value==false);
foreach(tmp中的MyClass mc){
mc.BeforeDelete();
myInserts[lt]=真;
}
//在调用base.SubmitChanges()之前,请确保未更改任何其他内容:
Sy