将LINQ调试为SQL SubmitChanges()

将LINQ调试为SQL SubmitChanges()

Debugging LINQ to SQL SubmitChanges()

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

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

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

1
2
3
4
5
JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID ="New Project";
this.UpdateJobMaster(newJobToCreate);
this.SubmitChanges();

运行this.SubmitChanges;时,我会捕获一些非常奇怪的异常。

1
Index was outside the bounds of the array.

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

1
2
3
4
5
6
7
8
9
10
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

编辑2:
根据sirrocco的建议,我已更改为InsertOnSubmit,但仍收到相同的错误。

编辑3:
我已经实施了Sam的建议,试图记录生成的SQL并捕获ChangeExceptoinException。这些建议不再引起人们的注意,抛出异常时,我实际上从未生成过SQL。

编辑4:
我在下面找到了一个对我有用的答案。它只是一个理论,但是已经解决了我当前的问题。


我总是发现对确切地知道将什么更改发送到SubmitChanges()方法中到DataContext有用。

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

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

1
ctx.GetChangeSet();

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


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

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

在执行此操作时,我注意到了几件事:

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

这只是一个理论。如果有人可以更好地解释它,我希望在这里有一个具体的答案。


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

1
2
3
4
5
6
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并查看失败的详细信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  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);
      }
    }
  }

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

我发现这在调试LINQ to SQL异常时非常有用。

1
2
3
4
5
6
partial void OnCreated()
{
#if DEBUG
      this.Log = Console.Out;
#endif
}

这就是我所做的

1
2
3
4
5
6
7
8
9
10
11
12
...
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);
}

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

如果他们抛出一个更具描述性的例外,那就太好了,也许他们会在将来的版本中发布。 (达米恩/马特...?)


这几乎肯定不是所有人的根本原??因,但是我在项目中遇到了完全相同的异常-并发现根本原因是在构造实体类时引发了异常。奇怪的是,真正的异常是"丢失"的,而是表现为ArgumentOutOfRange异常,该异常起源于检索对象的Linq语句的迭代器。

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


VS 2008可以通过.NET框架进行调试(http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code .aspx)

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


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

1
2
3
4
5
JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID ="New Project";
this.InsertOnSubmit(newJobToCreate);
this.SubmitChanges();

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

当然,这只有在您克服了例外之后才有帮助。


检查dbml中的所有"主键"列是否确实与数据库表上的主键相关。我只是遇到一种情况,设计人员决定在dbml中添加一个额外的PK列,这意味着LINQ to SQL在保存时找不到外键的两面。


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

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


我今天早些时候在这里发布了一个类似的问题:奇怪的LINQ异常(索引超出范围)。

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

如果问题中的数据组合有助于撒马利亚人很好地回答,则在此问题中交叉发布:)


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

1
2
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语句以在同一查询中接收自动生成的值,并期望第一个记录集包含"要求"列,但在这种情况下,第一行是 触发器中的select语句中的列。 所以linq-to-sql期望有两个自动生成的列,但是它只接收到一个列(数据错误),这导致了此异常。


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

1
2
3
4
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);

代替:

1
2
3
4
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"对象在另一个"数据类"对象的实例中设置属性。 LINQ引擎可能无法检查INSQ命令是否允许我在" proce"对象中设置的属性,而INSERT命令必须由LINQ创建,以将另一个"数据类"对象添加到数据库中。


我有同样的非说话错误。

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

希望这对任何人有帮助!


在对SO#237415的答复中发布了我的例外情况经验


当尝试调试LINQ ChangeConflictException时,我遇到了这个问题。最后,我意识到问题是我在DBML文件中的表中手动添加了一个属性,但是我忘记设置诸如Nullable(在我的情况下应该为真)和Server Data Type之类的属性。

希望这对某人有帮助。


实际上,我们已经停止对大型项目使用Linq to SQL设计器,而这个问题是主要原因之一。我们还更改了许多默认的名称,数据类型和关系默认值,设计人员有时会丢失这些更改。我从来没有找到确切的原因,也无法可靠地复制它。

那,连同其他限制,导致我们放弃了设计师并手工设计课程。在习惯了模式之后,实际上比使用设计器要容易。


推荐阅读