【问题标题】:Why (on a specific server) would ADO,NET get a Time Out exception after db result is returned and the reader is being closed?为什么(在特定服务器上)ADO,NET 在返回 db 结果并关闭阅读器后会出现超时异常?
【发布时间】:2026-01-09 19:25:01
【问题描述】:

我正在尝试修复的 ASP.NET 应用程序中发生了一些奇怪的行为。

我从代码中收到以下错误:

超时。在操作完成之前超时时间已过或服务器没有响应。

我熟悉此错误及其许多原因。我所有典型的故障排除途径都失败了。

以下是一些动态:

服务器最近重建。所以可能是服务器配置问题。

此错误仅发生在特定的 Web 服务器上。当我在本地运行应用程序时,从其他服务器运行它很快。我无法从我的机器上重新解决 MSSMS 中 proc 的性能问题。

  • 这告诉我它是特定于该服务器的。

当我使用 OSql 命令行实用程序运行相同的过程时,它可以工作。快。

  • 这表明它可能与 .NET 相关,而不是 db 相关

在执行此代码之前,我已在此服务器和同一数据库上执行了其他存储过程。

  • 这表明 proc 可能是相关的,但不是“服务器 X 无法与服务器 Y 通信”

我已经从数据库所有者那里得到确认,数据库已收到命令,执行(>1 秒)它并返回数据。

  • 通过跟踪代码,我看到返回了结果,直到我尝试关闭数据读取器才发生错误。
  • 事实上,我发现执行存储过程并遍历结果需要 36 毫秒。

看起来对 DataReader.Close 的调用需要时间,最终会超时。

我已将最大池从 31 增加到 100。

这里是我的代码的样例,它的结构是怎样的。我一直在破解它以进行故障排除:我添加了显式关闭以确保我知道错误发生在哪里。可能存在语法问题:我已将其设为通用,并且可能在这样做时引入了错误。

    public double  GetMyData()
    {
        double returnValue;
       // Used in logging to see if code was reached & how long it took.
      System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch(); 
            using (SqlConnection cn = Connections.GetSqlConnection())
        {
            cn.Open();
            using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
            {

                Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
                s.Start();
                SqlDataReader dr= null;
                try
                {
                     dr = cmd.ExecuteReader();
                     s.Stop();
                     Log.Log.WriteTrace("Timer", "ExecuteReader done " + s.ElapsedMilliseconds + "ms ", 0);
                     s.Start();


                    if (dr != null)
                    {
                        if (dr.Read())
                        {
                            returnValue =
                                Conversion.DBNullToDouble(
                                    dr[0]);
                        }
                        s.Stop();
                        Log.Log.WriteTrace("Timer", "dr.read done  (result:" + returnValue + ")" + s.ElapsedMilliseconds + "ms ", 0); // I get Here
                    }
                }catch(Exception ex)
                {
                    Log.Log.PersistException(ex);
                }
                //}
                if(dr!=null && !dr.IsClosed)
                    dr.Close();// This times out
                if (cn != null && cn.State !=ConnectionState.Closed)
                        cn.Close();
                Log.Log.WriteTrace("DONE "),
                                 ;
            }
        }
            return (returnValue);
    }

更新

dr.Close(); 需要 2 分钟才能执行。就在这台服务器上。在本地只需不到一秒钟。

更新

根据接受的答案的 cmets:我有一个包含多条记录的 proc。我拿的是拳头。调用 cmd.Cancel() 并没有修复,但大大减少了关闭数据读取器所需的时间。探索这一点应该可以帮助我解决问题。我不知道为什么这只发生在这台服务器上,因为服务器是开发服务器。

【问题讨论】:

  • 您是否尝试在 using 语句中也使用它?
  • 感谢您的评论。我有,原始代码依赖于 Using 语句来管理关闭阅读器和连接。我更改了它,因为错误发生在编译器发出的 IL 中。引发错误的源代码是“}”。我将其转换为显式关闭以确保没有隐藏错误。

标签: asp.net ado.net


【解决方案1】:

我在您的代码中发现了一些问题。

  1. 尝试在 using 语句中使用 Reader,它会在完成时关闭并释放。
  2. 您正在关闭 2 次连接(使用时

    if (cn != null && cn.State !=ConnectionState.Closed) cn.Close();using (SqlConnection cn = Connections.GetSqlConnection()) 中——这个在声明的末尾为你做这个

  3. 在您的代码中,您没有检查 DataReader.HasRows 是否为超时异常

  4. 如果您只想检索第一行第一列的值,您应该查看ExecuteScalar

终于 我会像这样重写你的代码(使用 DataReader)

public double  GetMyData()
    {
        double returnValue;
       // Used in logging to see if code was reached & how long it took.
      System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch(); 
            using (SqlConnection cn = Connections.GetSqlConnection())
        {
            cn.Open();
            using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
            {

                Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
                s.Start();
                using(SqlDataReader dr = cmd.ExecuteReader())
                {


                     s.Stop();
                     Log.Log.WriteTrace("Timer", "ExecuteReader done " + s.ElapsedMilliseconds + "ms ", 0);
                     s.Start();


                    if (dr != null)
                    {
                        if(dr.HasRows)
                        {
                           if (dr.Read())
                           {
                              returnValue =
                                Conversion.DBNullToDouble(
                                    dr[0]);
                           }
                        }
                        s.Stop();
                        Log.Log.WriteTrace("Timer", "dr.read done  (result:" + returnValue + ")" + s.ElapsedMilliseconds + "ms ", 0); // I get Here
                    }
                }             
            }
        }
            return (returnValue);
    }

或(使用 ExecuteScalar)

public double  GetMyData()
        {
            double returnValue;
           // Used in logging to see if code was reached & how long it took.
          System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch(); 
                using (SqlConnection cn = Connections.GetSqlConnection())
            {
                cn.Open();
                using (SqlCommand cmd = getSQLCommmand("SomeProcName"))
                {

                    Log.Log.WriteTrace(string.Format("Execute {0} ","SomeProcName"),0);
                    s.Start();
                    try
                    {
                         returnValue = Conversion.DBNullToDouble(cmd.ExecuteScalar());
                         s.Stop();  
                    }   
                    catch(Exception ex)
                    {
                        Log.Log.PersistException(ex);
                    }

                }
            }
                return (returnValue);
        }

【讨论】:

  • 谢谢。你提出了一些好的观点。我的问题是代码在我的机器上运行良好,并且(我之前没有提到这一点)已经在生产环境中工作了好几年。我不明白为什么在我得到结果后会发生异常,并且只在一台服务器上发生。 RE:HasRows-Reader.Read() 如果没有数据将不会抛出异常。它返回假。 HasRows 是一种无需实际读取数据即可确定读取器是否“有行”的方法。 HasRows 没有副作用。
  • 阅读前无需检查hashrows。如果没有行,Read 将返回 false
  • 嗯,您的 proc 没有返回超过 1 行?如果这种情况已经好几年了,我认为你的存储过程发生了变化,或者在你改变了生产服务器之后它开始失败了?
  • 这个过程返回几个。我们只关心第一排。 [长话短说。:) ] 请记住,它 100% 可以在其他服务器上运行!
  • mm 看看这篇文章,*.com/questions/133374/… 我无法解释为什么它可以在其他服务器上运行,但据我所知,如果你没有完全迭代它,你可能不会关闭阅读器,这就是为什么我问你存储过程是否返回超过 1 行。我想您不想仅仅因为一个服务器不起作用就更改每台服务器上的代码。您是否有权访问数据库来修改您的存储过程以仅返回那一行并看看会发生什么?这样读者将被完全迭代
最近更新 更多