调试LINQ to SQL SubmitChanges()

时间:2008-09-17 19:27:37

标签: linq linq-to-sql debugging

我很难尝试调试LINQ to SQL并提交更改。

我一直在使用http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx,它非常适合调试简单查询。

我正在使用我的应用程序中的以下代码段为我的项目使用DataContext类:

JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.UpdateJobMaster(newJobToCreate);
this.SubmitChanges();

当我运行this.SubmitChanges时,我会发现一些非常奇怪的异常;

Index was outside the bounds of the array.

堆栈跟踪进入了我无法进入的地方:

at System.Data.Linq.IdentityManager.StandardIdentityManager.MultiKeyManager`3.TryCreateKeyFromValues(Object[] values, MultiKey`2& k)
   at System.Data.Linq.IdentityManager.StandardIdentityManager.IdentityCache`2.Find(Object[] keyValues)
   at System.Data.Linq.IdentityManager.StandardIdentityManager.Find(MetaType type, Object[] keyValues)
   at System.Data.Linq.CommonDataServices.GetCachedObject(MetaType type, Object[] keyValues)
   at System.Data.Linq.ChangeProcessor.GetOtherItem(MetaAssociation assoc, Object instance)
   at System.Data.Linq.ChangeProcessor.BuildEdgeMaps()
   at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)
   at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
   at System.Data.Linq.DataContext.SubmitChanges()
   at JobTrakDataContext.CreateNewJob(NewJob job, String userName) in D:\JobTrakDataContext.cs:line 1119

有没有人使用过他们使用的工具或技术?我错过了一些简单的东西吗?

修改: 我使用Slace的建议设置.net调试,但.net 3.5代码尚不可用:http://referencesource.microsoft.com/netframework.aspx

EDIT2 : 根据sirrocco的建议,我已经改为InsertOnSubmit,仍然会出现同样的错误。

EDIT3: 我已经实现了Sam的建议,试图记录生成的SQL并捕获ChangeExceptoinException。这些建议没有任何消息,当我抛出异常时,我实际上从未实际生成SQL。

EDIT4: 我找到了一个对我有用的答案。它只是一个理论,但它解决了我目前的问题。

19 个答案:

答案 0 :(得分:31)

我总是觉得有用的是确切知道在SubmitChanges()方法中发送到DataContext的更改。

我使用DataContext.GetChangeSet()方法,它返回一个ChangeSet对象实例,该实例包含已添加,修改或删除的3个只读IList对象。

您可以在SubmitChanges方法调用之前放置一个断点,并添加一个包含以下内容的Watch(或Quick Watch):

ctx.GetChangeSet();

其中ctx是DataContext的当前实例,然后您将能够跟踪在SubmitChanges调用中有效的所有更改。

答案 1 :(得分:8)

首先,感谢大家的帮助,我终于找到了它。

解决方案是从项目中删除.dbml文件,添加一个空白.dbml文件,并使用“服务器资源管理器”中的项目所需的表重新填充它。

我在做这件事时发现了一些事情:

  • 系统中有几个表以两个单词命名,单词之间有一个空格,即“Job Master”。当我将该表拉回到.dbml文件中时,它将创建一个名为“Job_Master”的表,它将用下划线替换该空格。
  • 在原始.dbml文件中,我的一个开发人员已经浏览了.dbml文件并删除了所有下划线,因此'Job_Master'将成为.dbml文件中的'JobMaster'。在代码中,我们可以在更多的标准命名约定中引用该表。
  • 我的理论是在某个地方,从“JobMaster”到“Job Master”的翻译虽然在进行投影时丢失了,但我仍然不断提出数组越界错误。

这只是一种理论。如果有人能够更好地解释它,我希望在这里得到具体答案。

答案 2 :(得分:6)

我的第一个调试操作是查看生成的SQL:

JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.UpdateJobMaster(newJobToCreate);
this.Log = Console.Out; // prints the SQL to the debug console
this.SubmitChanges();

第二个是捕获ChangeConflictException并查看失败的细节。

  catch (ChangeConflictException e)
  {
    Console.WriteLine("Optimistic concurrency error.");
    Console.WriteLine(e.Message);
    Console.ReadLine();
    foreach (ObjectChangeConflict occ in db.ChangeConflicts)
    {
      MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType());
      Customer entityInConflict = (Customer)occ.Object;
      Console.WriteLine("Table name: {0}", metatable.TableName);
      Console.Write("Customer ID: ");
      Console.WriteLine(entityInConflict.CustomerID);
      foreach (MemberChangeConflict mcc in occ.MemberConflicts)
      {
        object currVal = mcc.CurrentValue;
        object origVal = mcc.OriginalValue;
        object databaseVal = mcc.DatabaseValue;
        MemberInfo mi = mcc.Member;
        Console.WriteLine("Member: {0}", mi.Name);
        Console.WriteLine("current value: {0}", currVal);
        Console.WriteLine("original value: {0}", origVal);
        Console.WriteLine("database value: {0}", databaseVal);
      }
    }
  }

答案 3 :(得分:4)

您可以为DataContext创建一个分部类,并使用Created或者您有部分方法来设置包含在#if DEBUG中的console.out日志..这将帮助您查看调试时执行的查询您正在使用的任何datacontext实例。

我在调试LINQ to SQL异常时发现这很有用..

partial void OnCreated()
{
#if DEBUG
      this.Log = Console.Out;
#endif
}

答案 4 :(得分:2)

您在上面提到的错误通常是由指向错误方向的 关联引起的 。当手动向设计器添加关联时,这很容易发生,因为与数据建模工具相比,L2S设计器中的关联箭头指向后方。

如果他们抛出更具描述性的异常会很好,也许他们会在将来的版本中。 (Damien / Matt ......?)

答案 5 :(得分:2)

这就是我做的事情

...
var builder = new StringBuilder();
try
{
    context.Log = new StringWriter(builder);
    context.MY_TABLE.InsertAllOnSubmit(someData);
    context.SubmitChanges();                
}
finally
{
    Log.InfoFormat("Some meaningful message here... ={0}", builder);
}

答案 6 :(得分:1)

VS 2008能够通过.NET框架(http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx

进行调试

这可能是您最好的选择,您可以看到正在发生的事情以及所有属性在确切时间点的位置

答案 7 :(得分:1)

为什么要在新实例上执行UpdateJobMaster?不应该是InsertOnSubmit吗?

JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.InsertOnSubmit(newJobToCreate);
this.SubmitChanges();

答案 8 :(得分:1)

一个简单的解决方案可能是在您的数据库上运行跟踪并检查针对它运行的查询 - 过滤使用其他应用程序以及访问数据库等。

只有当你超越例外时才会有帮助...

答案 9 :(得分:1)

这几乎肯定不是每个人的根本原​​因,但我在我的项目中遇到了这个完全相同的异常 - 并且发现根本原因是在构造实体类期间抛出了异常。奇怪的是,真正的异常是“丢失”,而是表现为一个ArgumentOutOfRange异常,它起源于检索对象的Linq语句的迭代器。

如果您收到此错误并且已在POCO上引入了OnCreated或OnLoaded方法,请尝试逐步执行这些方法。

答案 10 :(得分:0)

我今天早些时候在这里发布了一个类似的问题:Strange LINQ Exception (Index out of bounds)

这是一个不同的用例 - 在SubmitChanges()期间发生此错误,我在简单查询期间发生,但它也是索引超出范围错误。

如果问题中的数据组合有助于善意的撒玛利亚人回答,则在此问题中交叉发帖:)

答案 11 :(得分:0)

这是很久以前的事了,但是我遇到了同样的问题,错误是由于带有select语句的触发器引起的。像

CREATE TRIGGER NAME ON TABLE1 AFTER UPDATE AS SELECT table1.key from table1 
inner join inserted on table1.key = inserted.key

当linq-to-sql运行update命令时,它还会运行select语句以在同一查询中接收自动生成的值,并期望第一个记录集包含“要求的”列,但在这种情况下,第一个记录row是触发器中select语句的列。因此linq-to-sql期望有两个自动生成的列,但它只接收到一个列(数据错误),这导致了此异常。

答案 12 :(得分:0)

我在尝试调试LINQ ChangeConflictException时最终遇到了这个问题。最后我意识到问题是我在我的DBML文件中手动添加了一个属性,但我忘了设置像 Nullable 这样的属性(在我的情况下应该是真的)和服务器数据类型

希望这有助于某人。

答案 13 :(得分:0)

在SO#237415

的回答中发表了我对此例外的经验

答案 14 :(得分:0)

我有同样的非说话错误。

我有一个表的列的外键关系,它不是表的主键,而是一个唯一的列。 当我将唯一列更改为表的主键时,问题就消失了。

希望这对任何人都有帮助!

答案 15 :(得分:0)

我最近遇到了同样的问题:我做的是

Proce proces = unit.Proces.Single(u => u.ProcesTypeId == (from pt in context.ProcesTypes
                                                                         where pt.Name == "Fix-O"
                                                                         select pt).Single().ProcesTypeId &&
                                                                      u.UnitId == UnitId);

而不是:

Proce proces = context.Proces.Single(u => u.ProcesTypeId == (from pt in context.ProcesTypes
                                                                     where pt.Name == "Fix-O"
                                                                     select pt).Single().ProcesTypeId &&
                                                                  u.UnitId == UnitId);

上下文显然是DataContext对象,“unit”是Unit对象的实例,是dbml文件中的数据类。

接下来,我使用“proce”对象在另一个Data Class对象的实例中设置属性。可能LINQ引擎无法检查我在“proc”对象中设置的属性是否允许在INSERT命令中,该命令必须由LINQ创建,以将其他Data Class对象添加到数据库中。

答案 16 :(得分:0)

检查dbml中的所有“主键”列是否与数据库表上的主键实际相关。我只是遇到设计师决定在dbml中添加额外PK列的情况,这意味着LINQ to SQL在保存时无法找到外键的两面。

答案 17 :(得分:0)

我们实际上已停止使用Linq to SQL设计器来处理大型项目,这个问题是主要原因之一。我们还更改了名称,数据类型和关系的许多默认值,设计师每隔一段时间就会丢失这些更改。我从来没有找到确切的理由,我无法可靠地重现它。

这与其他限制一起导致我们放弃设计师并手工设计课程。在我们习惯了模式之后,它实际上比使用设计器更容易。

答案 18 :(得分:0)

槽糕。

采用WAG(Wild Ass Guess),它看起来像LINQ - SQL试图找到一个id不存在的对象,基于JobMaster类的创建。是否存在与该表相关的外键,以便LINQ to SQL将尝试获取可能不存在的类的实例?您似乎将新对象的ProjectID设置为字符串 - 您真的有一个字符串的id吗?如果您尝试将其设置为新项目,则需要创建一个新项目并获取其ID。

最后,UpdateJobMaster做了什么?它可以做一些上述适用的事情吗?