2014-11-04 83 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