4

嘿,我正在從Java遷移到C#,現在我已經意識到我更喜歡C#語言特性而不是Java語言特性,但是我有這個小問題。在MySQL Connector/J和JDBC中,我相信我的一個應用程序允許在另一個打開時執行多個PreparedStatement s,就像我可以執行返回ResultSet的查詢並且該ResultSet仍然打開時,我可以打開另一個PreparedStatement並得到另一個ResultSet或者我可以執行一個更新,根據我從我的第一個ResultSet得到的數據(即,插入一個salt值並用SHA512哈希更新密碼列時,我意識到該行中有明文密碼密碼欄)。MySQL連接器/ NET連接每個連接多個DataReader?

然而,連接器/ NET,我已經認識到,每當我嘗試這樣做,我得到這個錯誤: MySql.Data.MySqlClient.MySqlException: There is already an open DataReader associated with this Connection which must be closed first.

有沒有一種簡單的方法來解決這個錯誤,也許的任何其他實現MySQL到.NET橋樑?我並不想在一個應用程序中創建很多數據庫連接,儘管我可能想爲應用程序中的每個線程創建一個(如在ThreadLocal中)。當我用兩種不同的方法同時執行兩個查詢時,ThreadLocal DB連接將會有所幫助,但顯然,我無法將這兩個命令分離到不同的線程中,而且我也不想創建多餘的線程。

順便說一下,這裏是代碼本身。是的,我可以向下移動的更新代碼後,我關閉了讀者,但我有更多的類似的方法,其中一些更困難比這個來修復:

MySqlConnection con = DatabaseConnection.GetConnection(); 
MySqlCommand cmd = con.CreateCommand(); 
cmd.CommandText = "SELECT `id`,`password`,`salt`,`pin`,`gender`,`birthday` FROM `accounts` WHERE `name` = '" + AccountName + "'"; 
MySqlDataReader reader = cmd.ExecuteReader(); 
if (reader.Read()) 
{ 
    AccountId = reader.GetInt32(0); 
    string passhash = !reader.IsDBNull(1) ? reader.GetString(1) : null; 
    string salt = !reader.IsDBNull(2) ? reader.GetString(2) : null; 
    m_pin = !reader.IsDBNull(3) ? reader.GetString(3) : null; 
    Gender = !reader.IsDBNull(4) ? reader.GetByte(4) : WvsCommon.Gender.UNDEFINED; 
    m_birthday = !reader.IsDBNull(5) ? reader.GetInt32(5) : 0; 
    if (!HashFunctions.HashEquals(pwd, HashAlgorithms.SHA512, passhash + salt)) 
    { 
     if (passhash == pwd || salt == null && HashFunctions.HashEquals(pwd, HashAlgorithms.SHA1, passhash)) 
     { 
      salt = HashFunctions.GenerateSalt(); 
      passhash = HashFunctions.GenerateSaltedSha512Hash(pwd, salt); 
      MySqlCommand update = con.CreateCommand(); 
      update.CommandText = "UPDATE `accounts` SET `password` = '" + passhash + "', `salt` = '" + salt + "' WHERE `id` = " + AccountId; 
      update.ExecuteNonQuery(); 
      update.Dispose(); 
     } 
    } 
} 
reader.Close(); 
cmd.Dispose(); 

如果移動的更新代碼唯一的可能性,或者如果它是最好的,我想我必須做到這一點,但我想先獲得關於其他可能性的更多想法,然後選擇一個選項。

回答

2

不,我敢打賭,在Java世界中也是如此。

連接正在積極使用/ HOLD檢索數據,如果是在Java世界中的工作,是因爲它做了一個:

  • 讀/緩存整個結果集
  • 做到了在一個單獨的連接幕後

我沒有看到太多的問題,你只需要移動reader.Close到你的代碼中的適當位置。也就是說,無論如何您都應該通過該代碼,因爲如果發生異常,您的處置/關閉調用將不會被正確調用。使用using語句,以確保一切適當解脫出來,下面的代碼與這些變化(以及一些其他人,使其不太深向右)修改後的版本:

using(MySqlConnection con = DatabaseConnection.GetConnection()) 
using(MySqlCommand cmd = con.CreateCommand()) 
{ 
    cmd.CommandText = "SELECT `id`,`password`,`salt`,`pin`,`gender`,`birthday` FROM `accounts` WHERE `name` = '" + AccountName + "'"; 
    using(MySqlDataReader reader = cmd.ExecuteReader()) 
    { 
     if(!reader.Read()) return; 
     AccountId = reader.GetInt32(0); 
     string passhash = !reader.IsDBNull(1) ? reader.GetString(1) : null; 
     string salt = !reader.IsDBNull(2) ? reader.GetString(2) : null; 
     m_pin = !reader.IsDBNull(3) ? reader.GetString(3) : null; 
     Gender = !reader.IsDBNull(4) ? reader.GetByte(4) : WvsCommon.Gender.UNDEFINED; 
     m_birthday = !reader.IsDBNull(5) ? reader.GetInt32(5) : 0; 
     reader.Close(); 
     if (HashFunctions.HashEquals(pwd, HashAlgorithms.SHA512, passhash + salt)) 
      return; 
     if(passhash != pwd && !(salt == null && HashFunctions.HashEquals(pwd, HashAlgorithms.SHA1, passhash))) 
      return; 
     salt = HashFunctions.GenerateSalt(); 
     passhash = HashFunctions.GenerateSaltedSha512Hash(pwd, salt); 
     using(MySqlCommand update = con.CreateCommand()) 
     { 
      update.CommandText = "UPDATE `accounts` SET `password` = '" + passhash + "', `salt` = '" + salt + "' WHERE `id` = " + AccountId; 
      update.ExecuteNonQuery(); 
     } 
    } 
} 
+0

只是一個快速的新手問題,如果reader.Read()返回false,那麼我們不必對它做一個reader.Close()? – 2010-09-12 15:56:20

+0

@Kevin the Close會自動發生,因爲讀者的Dispose是用using語句調用的---這取決於你如何構造代碼,你不需要明確地調用它。 – eglasius 2010-09-12 16:00:11

+0

好的,謝謝!由於這一點,我想我對MySQL Connector/NET有了更好的理解。我想JDBC的ResultSet實際上只是一個存儲所有數據的表,然後關閉PreparedStatement,以便其他查詢和非查詢可以在之後執行,這幾乎是唯一的解釋。 – 2010-09-12 16:24:55

0

MSDN

While the SqlDataReader is being used, the associated SqlConnection is busy serving the SqlDataReader, and no other operations can be performed on the SqlConnection other than closing it. This is the case until the Close method of the SqlDataReader is called. For example, you cannot retrieve output parameters until after you call Close

我通常如何解決這個是我的窩連接,我需要這樣,當我第一個使用關閉所有其他連接設置。

+0

出於某種原因,我需要一整秒才能建立到我的MySQL數據庫的連接,除非我正在做的事情完全錯誤。 事情是,我試圖創建一個服務器,我想嘗試與我的客戶實時保持一秒鐘,而且相當多的時間。對於每個線程,JDBC似乎首次爲250毫秒左右建立連接。 – 2010-09-12 15:42:28

+0

沒關係,似乎每個後續的數據庫新連接看起來都要快得多。我試圖通過使數據庫連接ThreadStatic,然後用MySQL更新執行一個新線程,而另一個線程有一個Reader仍然打開。沒有問題,我測量了11毫秒。 – 2010-09-12 16:28:01

2

好的傢伙,帶着幾分更多的研究,我意識到我錯了。 Java的結果集實際上保持與數據庫的活動連接,如本頁所示:www.geekinterview。com/question_details/591

ResultSets必須連接,以便ResultSet.next()方法可以正常工作以從數據庫中獲取下一行。請注意,這並不意味着連接忙於爲ResultSet服務,而是ResultSet僅保留在連接上,以便在給定命令時可以向前移動。

顯然SQL服務器有類似的東西,允許您打開多個只讀,只進的查詢,而另一個打開在同一個連接上,稱爲MARS(多活動結果集)。 http://www.codeguru.com/csharp/csharp/cs_network/database/article.php/c8715

隨着多一點研究,我意識到MySQL連接器/ NET不支持此功能。這太糟糕了,因爲我相信它比目前的實現更有意義,至少對於Java開發人員的遷移而言。