5

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

但是,使用 Connector/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();

如果移动更新代码是唯一的可能性,或者如果它是最好的,我想我将不得不这样做,但我想先获得更多关于其他可能性的想法,然后再选择一个选项。

4

3 回答 3

2

好吧,伙计们,通过更多的研究,我意识到我错了。Java 的 ResultSets 实际上持有与数据库的活动连接,如以下页面所示:www.geekinterview.com/question_details/591

必须连接 ResultSet,这样 ResultSet.next() 方法才能正常工作以从数据库中获取下一行。请注意,这并不意味着连接正忙于为 ResultSet 提供服务,而是 ResultSet 只保留连接,以便在发出命令时它可以向前移动。

显然,SQL Server 具有与此类似的功能,它允许您在同一连接上打开多个只读、只进查询时打开另一个查询,称为 MARS(多个活动结果集)。 http://www.codeguru.com/csharp/csharp/cs_network/database/article.php/c8715

通过更多研究,我意识到 MySQL Connector/NET 不支持此功能。这太糟糕了,因为我相信它比当前的实现更有意义,至少对于迁移 Java 开发人员而言。

于 2010-10-06T02:43:25.967 回答
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();
        }
    }
}
于 2010-09-10T17:52:24.833 回答
0

来自MSDN

在使用 SqlDataReader 时,关联的 SqlConnection 正忙于为 SqlDataReader 服务,除了关闭 SqlConnection 之外,不能对它执行其他操作。在调用 SqlDataReader 的 Close 方法之前就是这种情况。例如,在调用 Close 之前,您无法检索输出参数

我通常用来解决这个问题的方法是嵌套我需要的连接,这样当我第一次使用关闭时,所有其他连接都会被处理掉。

于 2010-09-10T14:32:19.247 回答