为什么我的SqlCommand在应该是int时返回一个字符串?

时间:2009-05-19 13:33:49

标签: c# sql-server linq-to-sql ado.net

我有一个查询,总是返回一个int。我现在已经记录了它返回一个与它本应完全无关的字符串。

我们已经获得了一些随机的FormatExceptions,我们已经跟踪了几个数据库查询。经过一些额外的日志记录后,我发现今天早上,下面的查询返回了字符串“gladiator”。 Website.PkID是一个i​​nt列,大部分时间都可以工作,但有时它会失败并且返回一个int(那里有大于任何有效的WebsiteID)或随机字符串。

每个会话开始时会触发一次此特定查询。它没有使用共享连接,所以我很难理解它是如何得到这样的混合结果的。连接池中是否会出现某种损坏?

我认为这个问题与此查询无关。我也看到了来自LINQ查询的类似的FormatExceptions(因为意外的结果)。我们也在同一时间发现了一些错误:

  

将请求发送到服务器时发生传输级错误。 (提供者:TCP提供者,错误:0 - 远程主机强制关闭现有连接。

可能是连接问题吗?或者我们可能在数据库服务器和Web服务器之间混淆了结果集?这真让我摸不着头脑。

违规查询:

public static int GetActiveWebSiteID(string storeID, string statusID)
{
    int retval;

    string sql = @"SELECT isnull(MAX(PkID),0) FROM WebSite 
                   WHERE StoreID = @StoreID 
                   AND WebSiteStatusID = @WebSiteStatusID";

    SqlConnection conn = new SqlConnection(Settings.ConnString);
    SqlCommand cmd = new SqlCommand(sql, conn);
    cmd.CommandType = CommandType.Text;
    cmd.Parameters.AddWithValue("@StoreID", (object)storeID ?? DBNull.Value);
    cmd.Parameters.AddWithValue("@WebSiteStatusID", (object)statusID ?? DBNull.Value);

    conn.Open();
    using(conn)
    {
        var scalar = cmd.ExecuteScalar(); // <-- This value returned here should only ever be an int, but randomly is a string

        retval = Convert.ToInt32(scalar);
    }
    return retval;
}

上述查询直到最近一直运作良好。我们现在在应用程序中有一堆额外的LINQ查询(不确定这是否有所不同)。我们正在运行.Net 3.5。

9 个答案:

答案 0 :(得分:9)

在忽略了这个问题几个月之后,随着流量逐渐增加,它开始达到临界质量。我们增加了日志记录并发现了许多确定的实例,在这些实例中,在负载很重的情况下,完全不同的结果集将返回到不相关的查询。

我们在Profiler中查看了查询,并且能够看到错误的结果始终与相同的spid相关联,并且每个错误的结果总是在查询的实际sql语句后面的一个查询。这就像一个结果集被遗漏了,并且返回了spid中的下一个结果集(来自同一个池中的另一个连接)。疯狂。

通过反复试验,我们最终找到了一些SqlCommand或LINQ查询,这些查询的SqlConnection在使用后没有立即关闭。相反,通过一些源于对LINQ连接的误解的草率编程,DataContext对象仅在请求结束时而不是立即处理(并且连接关闭)。

一旦我们重构这些方法以立即关闭与C#“使用”块的连接(为下一个请求释放该池),我们就不再收到错误。虽然我们仍然不知道连接池会如此混淆的根本原因,但我们能够停止此类错误。此问题已与我发布的另一个类似错误一起解决,可在此处找到:What Causes "Internal Connection Fatal Errors"?

答案 1 :(得分:1)

ExecuteScalar()函数的返回类型为object,并使用var关键字声明结果变量。这不是一个很好的组合,因为你在系统上施加了很大的压力来正确地进行类型推理。

答案 2 :(得分:1)

我认为您正在考虑sqlCommand.ExecuteNonQuery返回int值中受影响的行数...

这是ExecuteScalar方法的定义:

public override object ExecuteScalar()
Member of System.Data.SqlClient.SqlCommand

<强>要点:

执行查询,返回查询返回的结果集中第一行的第一列。其他列或行将被忽略。

<强>返回:

结果集中第一行的第一列,如果结果集为空,则为空引用(在Visual Basic中为Nothing)。

所以,我认为返回该列的常用方法是作为列值的字符串表示。

答案 3 :(得分:1)

我最近看到过代码意外切换连接字符串的情况。出于诊断目的,请对连接字符串进行硬编码,然后查看问题是否消失。

另外,为了理智,请使用嵌套使用块,如:

using(SqlConnection conn = new SqlConnection("hard-coded connection string"))
{
    using (SqlCommand cmd = new SqlCommand(sql, conn))
    {
        // more init
        object scalar = cmd.ExecuteScalar();

        // process result
    }
 }

我发现有两个数据库实例并不令我感到惊讶,其中一个是PkID是一个i​​nt,另一个是varchar。


使用SQL事件探查器查看是否可以捕获“角斗士”的回归。在我正在使用的另一种情况下,SQL Profiler根本没有显示任何内容,表明实际的查询是进入不同的数据库。

答案 4 :(得分:0)

字段“PkID”是“WebSite”表中的varchar / char。

如果查询的“ISNULL”部分为真,那么它将返回一个整数(0),否则它将返回一个值为“PkID”的字符串

答案 5 :(得分:0)

可能有多个WebSite表。您可以使用模式名称限定表:

SELECT isnull(MAX(PkID),0)FROM YourSchema.WebSite                    WHERE StoreID = @StoreID                    AND WebSiteStatusID = @WebSiteStatusID

答案 6 :(得分:0)

当它无法返回int时是否有任何共性?

由于您的查询只返回单行中的单个列,如果您使用更多类型安全的ExecuteReader并获取第一列的值,您会得到什么?

它总是连续返回吗?如果WHERE子句导致它没有返回任何行(假设您的参数不是您认为的那样),则ISNULL不会生效 - 根本没有行,而ExecuteScalar是应该返回NULL。

答案 7 :(得分:0)

我认为你发布的查询和LINQ都不是问题所在。

你真的确定你正在寻找正确的来源吗?该方法如何调用?如何完成日志记录?

Select isn't broken.

答案 8 :(得分:0)

我假设从Web.Config或注册表中读取Settings.ConnString,并重用其他静态例程。是否有可能存在一个计时问题,即在例程中调用cmd.ExecuteScalar()之前执行第二个方法,该例程修改了连接上的cmd.CommandText?

希望这有帮助,

比尔