Linq2Sql:随机双插入bug - 一个真正的装腔作势者

时间:2012-11-19 16:06:27

标签: c# linq-to-sql .net-4.0

好的,您认为自己是真正的调试器吗?试试这个:

我有一个Linq2Sql项目突然间我们偶然发现,看似随机,我们得到同一行的双重插入。

我已将DataContext子类化并覆盖SubmitChanges。在那里,我在调用GetChangeSet()之前和之后都调用了base.SubmitChanges(),并使用文本文件记录器来记录Inserts集合中的对象。另外,我会长时间插入对插入对象的引用,以记录其自动编号ID。

当双重插入发生时,我在数据库中看到,每个都插入MyTableAMyTableB而不是每行插入一行。 SQL事件探查器显示四个插入语句,一个接一个地快速连续:

insert into MyTableA(...
insert into MyTableB(...
insert into MyTableA(...
insert into MyTableB(...

我签入了调试日志,Inserts集合中只有两个对象:一个 MyClassA一个 { {1}}。调用MyClassB后,更改集为空(应该是)。并且自动编号ID显示新插入行的更大值。

另一条有用的信息:在调试模式中单步执行时,从不发生错误;只有当你没有断点运行时。这让我怀疑它与执行速度有关。

我们一直使用相同的base.SubmitChanges()子类超过一年,我们之前从未见过这种行为。它只发生在DataContextMyClassA

总结:

  1. 从调试日志开始,一切看起来都正常。
  2. 在SQL事件探查器上,您可以看到正在发生双重插入。
  3. 这种行为经常发生,但不可预测,只发生在上面提到的两个类中,除了在调试模式下单步执行代码时不会发生这种情况。
  4. 编辑 - 新信息: 在我的MyClassB子类中,我有以下代码:

    DataContext

    try { base.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException) { // Automerge database values for members that client has not modified. foreach (ObjectChangeConflict occ in ChangeConflicts) { occ.Resolve(RefreshMode.KeepChanges); } } // Submit succeeds on second try. base.SubmitChanges(ConflictMode.FailOnFirstConflict); MyTableA都有一个强制性外键MyTableB引用OtherTableID。在更新公共父表OtherTable期间发生ChangeConflictException时会发生双重插入。

    我们现在有了气味......

3 个答案:

答案 0 :(得分:0)

当我遇到这样的问题之前,通常会遇到多个线程同时执行相同的代码。

您是否尝试过使用 lock {} 命令确保插件只被单个线程使用? MSDN Lock

答案 1 :(得分:0)

在Linq2Sql中看起来像是 BUG !这是一个可重复的实验:

using (var db1 = new MyDataContext()) {
  var obj1 = db1.MyObjects.Single(x => x.ID == 1);
  obj1.Field1 = 123;
  obj1.RelatedThingies.Add(new RelatedThingy {
                           Field1 = 456,
                           Field2 = "def",
                           });
  using (var db2 = new MyDataContext()) {
    var obj2 = db2.MyObjects.Single(x => x.ID == 1);
    obj2.Field2 = "abc";
    db2.SubmitChanges();
  }
  try {
    db1.SubmitChanges(ConflictMode.ContinueOnConflict);
  } catch (ChangeConflictException) {
    foreach (ObjectChangeConflict occ in ChangeConflicts) {
      occ.Resolve(RefreshMode.KeepChanges);
    }
  }
  base.SubmitChanges(ConflictMode.FailOnFirstConflict);
}

结果:ID = 1的MyObject记录更新,Field1值为123,Field2值为“abc”。还有两个新的,相同的记录插入到RelatedThingy,MyObjectID = 1,Field1 = 456,Field2 =“def”。

解释那个!

更新:在Microsoft Connect上记录此信息后,MS的优秀人员要求我整理一个突出显示该错误的小型演示项目。你不知道吗 - 我无法重现它。它似乎与我的项目的一些奇怪的特质有关。没有时间进一步调查,无论如何我找到了一个解决方法......

答案 2 :(得分:0)

FWIW,我们最近在SubmitChanges的重试逻辑中发现了这个问题。我们正在做InsertAllOnSubmit。发生ChangeConflictException时,我们会在每个Resolve(RefreshMode.KeepChanges,true)上使用ObjectChangeConflict重试。

我们以不同的方式重新编写工作(重试逻辑以重新执行整个事务),这似乎解决了问题。