#c# #mysql #.net
#c# #mysql #.net
Вопрос:
Я использую .NET Connector для доступа к базе данных MySQL из моей программы на C #. Все мои запросы выполняются с помощью MySqlCommand .BeginExecuteReader с результатами IAsyncResults, хранящимися в списке, чтобы я мог периодически проверять их и вызывать соответствующие обратные вызовы всякий раз, когда они заканчиваются, извлекая данные через MySqlCommand.EndExecuteReader. Я стараюсь никогда не держать один из этих ридеров открытым, пытаясь прочитать результаты из чего-то другого.
В основном это работает нормально. Но я обнаружил, что если я запускаю два запроса одновременно, то получаю страшное исключение MySqlException: с этим соединением уже связан открытый DataReader, который должен быть закрыт первым исключением в EndExecuteReader. И это происходит при первом вызове EndExecuteReader. Таким образом, сообщение об ошибке полно вздора; на данный момент нет другого открытого DataReader, если только соединитель каким-то образом не открыл его за кулисами без моего вызова EndExecuteReader. Так что же происходит?
Вот мой цикл обновления, включая обильное ведение журнала:
for (int i=queries.Count-1; i>=0; i--) {
Debug.Log("Checking query: " queries[i].command.CommandText);
if (!queries[i].operation.IsCompleted) continue;
var q = queries[i];
queries.RemoveAt(i);
Debug.Log("Finished, opening Reader for " q.command.CommandText);
using (var reader = q.command.EndExecuteReader(q.operation)) {
try {
q.callback(reader, null);
} catch (System.Exception ex) {
Logging.LogError("Exception while processing: " q.command.CommandText);
Logging.LogError(ex.ToString());
q.callback(null, ex.ToString());
}
}
Debug.Log("And done with callback for: " q.command.CommandText);
}
И вот журнал:
Как вы можете видеть, я запускаю оба запроса в быстрой последовательности. (Это первое, что делает моя программа после открытия соединения с БД, просто чтобы определить, что происходит.) Затем первый, который я проверяю, говорит, что это сделано, поэтому я вызываю EndExecuteReader на нем, и бум — он уже утверждает, что есть еще один открытый. Это происходит немедленно, еще до того, как он попадет в мой метод обратного вызова. Как это может быть?
Недопустимо ли иметь два открытых запроса одновременно, даже если я вызываю EndExecuteReader только по одному за раз?
Комментарии:
1. Пожалуйста, включите STRICT ON , это плохой код.
2. Пожалуйста, уточните.
3. var q — это объект, а не mysqlcomand мне интересно, почему это не дает вам ошибки
4. «Я понимаю , чего бояться … исключение в EndExecuteReader» В шаблоне APM (который вы используете)
EndX
метод генерирует любые исключения, которые произошли во время асинхронной операции, так что это вполне ожидаемо. Сообщение об исключении было истинным в момент возникновения исключения, не обязательно, когда оно было добавлено в ваш код.5. @nbk, да, q — это объект, который содержит MySqlCommand в качестве поля.
Ответ №1:
Когда вы выполняете два запроса одновременно, у вас должно быть два объекта подключения. Почему? Каждое соединение может обрабатывать только один запрос одновременно. Похоже, что ваш код попал в какое-то состояние гонки, когда сработали некоторые из ваших параллельных запросов, а затем пара из них столкнулась и завершилась неудачей.
В любом случае ваша система будет более устойчивой в производстве, если вы сможете упростить свои последовательности запуска. На вашем месте я бы выполнял один запрос за другим, а не пытался запускать их все сразу. (Очевидно, если это вызывает реальные проблемы с производительностью, вам придется запускать их одновременно. Но делайте это простым, пока вам не понадобится, чтобы оно было сложным.)
Комментарии:
1. Ну, это, похоже, соответствует наблюдениям, но задокументировано ли это ограничение где-нибудь? Я бы спал лучше, если бы мог указать на документы, от которых зависит этот рефакторинг. Все, что я смог найти, это то, что у вас не может быть двух объектов Reader, открытых одновременно, но на данный момент я этого не делаю (несмотря на утверждение в тексте исключения).
2. Это задокументировано для MySqlConnector (альтернативный MySQL ADO.NET библиотека): mysqlconnector.net/troubleshooting/connection-reuse
3. @JoeStrout Кроме того, nuget.org/packages/MySqlConnector обеспечивает фактический асинхронный ввод-вывод через новые
ExecuteReaderAsync
API (которые возвращаютTask
и намного проще в использовании, чемBegin
/EndExecuteReader
). Я бы настоятельно рекомендовал переключиться.