Соединение MySQL Connector / NET, несколько DataReaders на Соединение - PullRequest
5 голосов
/ 10 сентября 2010

Эй, ребята, я перехожу с Java на C # теперь, когда я понял, что предпочитаю возможности языка C # по сравнению с Java, но у меня есть небольшая проблема.В MySQL, Connector / J и JDBC, я считаю, что одно из моих приложений позволяло выполнять несколько PreparedStatement с, пока другое открыто, как я мог бы выполнить запрос, который возвращает 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();

Если перемещение обновлениякод - единственная возможность, или, если она лучшая, я думаю, мне придется с этим справиться, но сначала я хочу получить больше идей о других возможностях, а затем выбрать вариант.

Ответы [ 3 ]

2 голосов
/ 10 сентября 2010

Нет, и я готов поспорить, что это также относится и к миру 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();
        }
    }
}
2 голосов
/ 06 октября 2010

Ладно, ребята, немного углубившись в исследования, я понял, что был неправ.ResultSets 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 Connector / NET не поддерживает эту функцию.Это слишком плохо, потому что я считаю, что это имеет больше смысла, чем текущая реализация, по крайней мере, для миграции Java-разработчиков.

0 голосов
/ 10 сентября 2010

С MSDN

Пока используется SqlDataReader, связанный SqlConnection занят обслуживающий SqlDataReader, и нет другие операции могут быть выполнены на SqlConnection кроме закрытия Это. Это так до закрытия метод SqlDataReader вызывается. Например, вы не можете получить выходные параметры до тех пор, пока вы не позвоните Закрыть

Как правило, для решения этой проблемы я вкладываю нужные мне подключения, чтобы при первом закрытии использования все остальные подключения были удалены.

...