MySQL Connector / NET连接每个连接是否有多个DataReader?

Kev*_*Jin 5 .net mysql database-connection sqldatareader mysql-connector

大家好,我已经从Java迁移到C#,因为我意识到我更喜欢C#语言功能,而不是Java中的功能,但是这个问题很小。在MySQL Connector / J和JDBC中,我相信我的一个应用程序允许PreparedStatement在打开另一个时执行多个s,就像我可以执行一个返回a的查询,ResultSetResultSet在仍然打开时,我可以打开另一个PreparedStatement并获取另一个ResultSet或者我可以根据我第一次获得的数据执行更新ResultSet(即,插入盐值,并在我意识到该行在密码列中包含明文密码的情况下,使用SHA512哈希更新密码列)。

但是,使用连接器/ 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();
Run Code Online (Sandbox Code Playgroud)

如果移动更新代码是唯一的可能,或者是最好的选择,我想我必须解决这个问题,但是我想先对其他可能性有更多的想法,然后再选择一个。

egl*_*ius 2

不,我敢打赌 Java 世界也是如此。

如果在 Java 世界中有效,则正在积极使用/保持连接来检索该数据,因为它执行了以下操作之一:

  • 读取/缓存整个结果集
  • 在幕后以单独的连接进行

我没有看到太多问题,您只需将阅读器移动到代码中的适当位置即可。也就是说,无论如何您都应该检查该代码,因为如果发生异常,您的 dispose/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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)