2014-11-04 115 views
1

所以我读了很多关于SqlDataReaders在.Net中没有正确处理的问题 - 我一直在争取“超时过期。超时时间在从池中获取连接之前已经过去了,这可能是因为所有池连接正在使用和最大池大小达到“的错误现在几天了。显然,我可以将最大池大小提高到30,000 - 但这并不能解决实际问题。.Net数据库如何正确关闭我的数据库连接?

正如我通过代码,我执行以下SQL查询:

select * from sys.dm_os_performance_counters 
where counter_name ='User Connections' 

cmd.Connection.Open(); 

线之后,将用户连接加1,然而,它永远不会返回除非我回收Web服务器上的应用程序池(此时所有来自网站的活动数据库连接都被终止)。

这里是我的代码:

public static DataTable SPExecuteDataTable(string[] ConnectionData, params object[] args) 
{ 
    SqlConnection conn = null; 
    SqlCommand cmd = null; 
    SqlDataReader dr = null; 
    try 
    { 
     conn = new SqlConnection(ConnectionData[1]); 
     cmd = new SqlCommand(ConnectionData[0], new SqlConnection(ConnectionData[1])); 
     cmd.CommandType = CommandType.StoredProcedure; 

     for (int i = 0; i < args.Length; i++) 
     { 
      SqlParameter Param = new SqlParameter(ConnectionData[i + 2], DBNullIfNull(args[i])); 
      cmd.Parameters.Add(Param); 
     } 

     cmd.Connection.Open(); 
     DataTable dt = new DataTable(); 

     using (dr = cmd.ExecuteReader()) 
     { 
      if (dr != null) 
       dt.Load(dr); 
      else 
       dt = null; 
     } 

     return dt; 
    } 
    catch (Exception e) 
    { 
     Exception x = new Exception(String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message)); 
     throw x; 
    } 
    finally 
    { 
     conn.Close(); 
     cmd.Connection.Close(); 
     dr.Close(); 
     conn.Dispose(); 
     cmd.Dispose(); 
     dr.Dispose(); 
    } 

到目前为止,我已经尝试(在我finally块等)显式地关闭连接,但不起作用。我也试过使用类似这样的语句:

using (SqlDataReader dr = blah blah blah) 
{ 
    //code here 
} 

但这也行不通。我的代码有什么问题,在这里?

回答

0

解决方案:

使用数据表!为了防止具有与该数据库通信为您的应用程序的非数据访问层,只是这样做在你的数据访问层:

using (SqlDataReader dr = cmd.ExecuteReader()) 
         { 
          if (dr != null) 
           dt.Load(dr); 
          else 
           dt = null; 
         } 
         return dt; 

然后,你可以操纵DT,但是你会喜欢您的解决方案的其余部分以及连接已经妥善处理。像魅力一样工作,幸运的是,数据表和数据记录器的代码非常相似,因此以这种方式修改应用程序相对来说是相当痛苦的。

3

优选的做法是在缠绕块using连接,命令和读取器:

using(SqlConnection conn = new SqlConnection(ConnectionData[1]) 
{ 
    using(SqlCommand cmd = new SqlCommand(ConnectionData[0], conn) 
    {              // ^-- re-use connection - see comment below 
     cmd.CommandType = CommandType.StoredProcedure; 

     for (int i = 0; i < args.Length; i++) 
     { 
      SqlParameter Param = new SqlParameter(ConnectionData[i + 2], DBNullIfNull(args[i])); 
      cmd.Parameters.Add(Param); 
     } 

     cmd.Connection.Open(); 
     DataTable dt = new DataTable(); 

     using (dr = cmd.ExecuteReader()) 
     { 
      if (dr != null) 
       dt.Load(dr); 
      else 
       dt = null; 
     } 

     return dt; 
    }  
} 

这样,他们都得到关闭,妥善处理。

虽然我觉得你的问题的心脏是,你每次都创建连接:

conn = new SqlConnection(ConnectionData[1]); 
cmd = new SqlCommand(ConnectionData[0], new SqlConnection(ConnectionData[1])); 
             ^---- creating a second connection 

最后,你失去了很多潜在的有价值的信息(堆栈跟踪等)通过创建一个新的异常,并抛出它,而不是重新抛出原始异常:

catch (Exception e) 
{ 
    Exception x = new Exception(String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message)); 
    throw x; 
} 

我要么让原始异常冒泡或包含原始异常的InnerException

catch (Exception e) 
{ 
    string message = String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message); 
    Exception x = new Exception(message, e); 
    throw x; 
} 
+0

谢谢,会试试看。为了澄清,冗余连接(您的文章结束)不是原始设置。我在更换TO时解决了问题。我只是改变了cmd =代码行,并用“conn”替换了“new sqlcon ...” - 问题依然存在。 – 2014-11-04 14:40:34

+0

非常感谢 - 仍然在测试这个......另外,你能解释一下如何在使用嵌套using语句时从函数中返回一个SqlDataReader吗?他们会不会自己处理? – 2014-11-04 14:47:42

+1

如果您要退货,则不会将其放入使用中。在这种模式中,调用者有责任处理读者,这就是为什么返回读者不是最佳做法 - 它将创建对象的责任分开处理。 – 2014-11-04 15:09:10