2

我很难调试一些 LINQ to SQL 代码,因为没有调用堆栈,而且我不知道错误发生在哪里。这似乎不是 SQL 错误 - 它在 C# 中,但通过搜索和其他所有内容,我找不到任何方法来确定哪个计算给出了错误。

源代码如下,您可以看到当我尝试插入一些新行时抛出的错误。调用堆栈为空,并且异常不包含有关问题所在的信息。

var groups =
    from record in startTimes
    group record by record.startTime
    into g
    select new
               {                                   
                   startTime = g.Key.GetValueOrDefault(),
                   totalTasks = g.Count(),
                   totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault(),
                   minDwell = g.Min(o => o.record.dwellTime).GetValueOrDefault(),
                   maxDwell = g.Max(o => o.record.dwellTime).GetValueOrDefault(),
                   avgDwell = g.Average(o => o.record.dwellTime).GetValueOrDefault(),
                   stdevDwell = g.Select(o => Convert.ToDouble(o.record.dwellTime)).StdDev(),
                   correct80 = g.Sum( o => o.record.correct80.GetValueOrDefault() ? 1 : 0),
                   wrong80 = g.Sum(o => o.record.wrong80.GetValueOrDefault() ? 1 : 0)
               };

var statistics = groups.AsEnumerable().Select(
    g => new e_activeSession()
             {
                 workerId = wcopy,
                 startTime = g.startTime,
                 totalTasks = g.totalTasks,
                 totalTime = g.totalTime,
                 minDwell = g.minDwell,
                 maxDwell = g.maxDwell,
                 avgDwell = g.avgDwell,
                 stdevDwell = g.stdevDwell,
                 total80 = g.correct80 + g.wrong80,
                 correct80 = g.correct80,
                 percent80 = g.correct80 / (g.correct80 + g.wrong80)                                 
             }
    );                                       

// Put these rows into the table                
_gzClasses.e_activeSessions.InsertAllOnSubmit(statistics);                
_gzClasses.SubmitChanges();

调用堆栈和通常无用的异常

这是异常的堆栈跟踪。如果有人能破译它,请告诉我,因为我不知道如何阅读这些匿名类型......

at System.Data.SqlClient.SqlBuffer.get_Double()
at System.Data.SqlClient.SqlDataReader.GetDouble(Int32 i)
at Read_<>f__AnonymousType2`9(ObjectMaterializer`1 )
at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at System.Data.Linq.Table`1.InsertAllOnSubmit[TSubEntity](IEnumerable`1 entities)

另外,我在 LINQPad 中运行了这个查询并得到了相同的错误和堆栈跟踪。最后,这是 LINQ to SQL 生成的数据库查询,该查询在错误发生前执行。我根本看不到任何双重转换。

SELECT COALESCE([t5].[value22],'1/1/0001 12:00:00 AM') AS [startTime], [t5].[value] AS [totalTasks], COALESCE([t5].[value2],0) AS [totalTime], COALESCE([t5].[value3],0) AS [minDwell], COALESCE([t5].[value4],0) AS [maxDwell], COALESCE([t5].[value5],0) AS [avgDwell], [t5].[value6] AS [correct80], [t5].[value7] AS [wrong80], [t5].[value22]
FROM (
    SELECT COUNT(*) AS [value], MAX([t4].[timeInSession]) AS [value2], MIN([t4].[dwellTime]) AS [value3], MAX([t4].[dwellTime]) AS [value4], AVG([t4].[dwellTime]) AS [value5], SUM([t4].[value3]) AS [value6], SUM([t4].[value]) AS [value7], [t4].[value2] AS [value22]
    FROM (
        SELECT 
            (CASE 
                WHEN (COALESCE([t3].[wrong80],0)) = 1 THEN @p4
                ELSE @p5
            END) AS [value], [t3].[workerID], [t3].[value2], [t3].[timeInSession], [t3].[dwellTime], [t3].[value] AS [value3]
        FROM (
            SELECT 
                (CASE 
                    WHEN (COALESCE([t2].[correct80],0)) = 1 THEN @p2
                    ELSE @p3
                END) AS [value], [t2].[wrong80], [t2].[workerID], [t2].[value] AS [value2], [t2].[timeInSession], [t2].[dwellTime]
            FROM (
                SELECT (
                    SELECT MAX([t1].[timeStamp])
                    FROM [dbo].[workerLog] AS [t1]
                    WHERE ([t1].[dwellTime] IS NULL) AND ([t1].[timeInSession] = @p0) AND ([t1].[workerID] = @p1) AND ([t1].[timeStamp] <= [t0].[timeStamp])
                    ) AS [value], [t0].[workerID], [t0].[dwellTime], [t0].[timeInSession], [t0].[correct80], [t0].[wrong80]
                FROM [dbo].[workerLog] AS [t0]
                ) AS [t2]
            ) AS [t3]
        ) AS [t4]
    WHERE [t4].[workerID] = @p6
    GROUP BY [t4].[value2]
    ) AS [t5]
4

2 回答 2

3

让我们阅读该调用堆栈:

at System.Data.SqlClient.SqlBuffer.get_Double() 
at System.Data.SqlClient.SqlDataReader.GetDouble(Int32 i) 
at Read_<>f__AnonymousType2`9(ObjectMaterializer`1 ) 
at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext() 
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext() 
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) 
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) 
at System.Data.Linq.Table`1.InsertAllOnSubmit[TSubEntity](IEnumerable`1 entities) 

InsertAllOnSubmit是栈顶。它称为ToList,它称为 List 的“ctor”,即构造函数。

该 List 构造函数枚举其参数(延迟查询),从而导致查询被解析。

在 List 构造函数中的枚举期间,我们尝试移动到查询中的下一个元素 ( WhereSelectEnumerableIterator.MoveNext) - 这是一个枚举数,由您在 AsEnumerable 之后调用 Select 创建。为了移动到新元素,我们实际上需要做的是更深入地研究查询 ( ObjectReader.MoveNext),它将根据数据库结果创建一些对象。


在这一点上,我想指出你没有说startTimes. 我猜startTimes是一个 LinqToSql 查询


因此,该数据库查询被转换为 DataReader(未在堆栈中捕获),我们从 DataReader ( Read_<>f__AnonymousType29(ObjectMaterializer 1 )) 中提取一行,以便我们可以将该行转换为查询结果的 .net 实例.

在从行中提取值的过程中,涉及到一个 double 值,所以我们尝试提取那个 ( .GetDouble(Int32 i)and .get_Double()), 。现在,我们知道 DataReader 可以将其值视为object[] (参考SqlDataReader.GetValues)。GetDouble 可以这样实现似乎是合理的:

//rubbish implementation, the point is - a cast from object to double is made
public double GetDouble(Int32 i)     {
  double result = (double) this.GetValues()[i];
  return result;
}

因此,数据读取器数组中的对象不会强制转换为双精度。 最可能的可能性是对象为空!

也有可能存在一些其他类型(字符串或字节 [] 或其他类型),但如果您使用 LinqToSql 的设计器制作 dbml 文件,并且数据库中的表仍然与该 dbml 一致,这不太可能。


我看到 Convert.ToDouble 不在调用堆栈中。这必须在数据库中完成。Convert.ToDouble 是 -never 实际上称为 -... 相反,它被翻译成 sql 并在那里运行。数据库中类型转换的规则不同。

  • 如果您尝试在 .net 中将 null 转换为 double,则会出现异常。
  • 如果您尝试在 sql 中将 null 转换为 double,则会得到 null。
于 2012-08-04T05:01:50.477 回答
1

好的,这就是我所看到的,异常中的第二行说

at System.Data.SqlClient.SqlDataReader.GetDouble(Int32 i)...

在您的代码中,您只转换为 double 一次:

  stdevDwell = g.Select(o => Convert.ToDouble(o.record.dwellTime)).StdDev(),

尝试在该行注释掉g.Select(...),看看你是否仍然得到异常。

于 2012-08-04T03:24:54.493 回答