Вы действительно можете использовать if
, но вы также должны иметь возможность использовать while
в таком случае без ошибок. Просто измените его на if
, но если вы сделаете то же самое в случае, когда у вас может быть более одной строки, вы получите ту же ошибку. Поэтому важно понять, почему произошло исключение.
Давайте рассмотрим ваш код:
while (myReader.Read())
{
info = myReader.GetString("info");
...
Пока все хорошо.
/**
* Write the relevant info in the LOGS
*/
connection.Close(); <---------- :S
connection.Open(); <---------- if I don´t do this I got some problems with the connection!!!!
Ой! Вы устранили свои «некоторые проблемы», но знаете ли вы, что это были за «некоторые проблемы»? Это на самом деле здесь, что вы сломали свой while
.
В тот момент, когда я сказал «пока все хорошо», вот что у вас происходит:
- У вас есть объект подключения. Это "открыто".
- У вас есть объект для чтения данных. Извлекает данные из соединения.
Теперь, согласно спецификации IDbCommand.ExecuteReader
(http://msdn.microsoft.com/en-us/library/68etdec0.aspx),, соединение используется для этого считывателя. Вы не можете ничего делать с этим соединением, кроме как закрывать его, пока вы не вызовете Close()
для считывателя, либо явно, либо через Dispose()
, что произойдет, если читатель будет назначен в блоке using
.
Теперь строго это не всегда так. Данная реализация всегда может дать вам больше, чем обещания интерфейса (в зависимости от подписи интерфейса или в соответствии с документацией). Многие реализации «освобождают» соединение для повторного использования, когда Read()
возвращает false (единственное, что я когда-либо реализовывал), SQLServer 2005 имеет опцию MultipleActiveResultSets=True
, которая позволяет одному и тому же соединению одновременно поддерживать более одного считывателя данных одновременно минусы). Тем не менее, за исключением этих нарушений правил, единственное, что мы можем определенно сделать с нашим соединением прямо сейчас, это позвонить по номеру Close()
.
По этой причине, когда вы пытались вызвать cmdLog.ExecuteNonQuery()
, у вас появились "некоторые проблемы" в виде сообщения об ошибке, потому что вы пытались использовать соединение, которое использовалось для чего-то другого.
Итак, вы «исправили» это, выполнив единственное, что вы могли сделать с соединением - закрыв его - и затем открыв снова. Для всех намерений и целей вам понадобится совершенно новое соединение!
Теперь, когда вы возвращаетесь к while
, он снова вызывает myReader.Read()
. Этот метод считывает из потока данных, поступающих из соединения, либо возвращает false (если результатов больше нет), либо создает набор полей на основе того, что он читает из этого потока.
Но это не так, потому что вы закрыли эту связь. Так что он «взрывается» с исключением, потому что вы пытаетесь читать из закрытого соединения, а это просто невозможно.
Теперь, когда вы заменили while
на if
, вы никогда не возвращались к операции Read()
, которую вы взломали, поэтому код снова работает.
Но что вы собираетесь делать, если вам когда-нибудь понадобится сделать это с набором результатов, который имеет более одной строки?
Вы могли бы использовать MARS. Смотрите http://msdn.microsoft.com/en-us/library/h32h3abf%28v=vs.80%29.aspx для этого. Честно говоря, я бы избегал этого, если что-то от этого не выиграет; у этого есть некоторые тонкие значения, которые могут действительно сбить с толку, если вы ударите их.
Вы можете отложить следующую операцию до закрытия набора читателей. В этом случае это будет просто небольшая экономия, если убрать ненужные Open()
и Close()
:
using(myReader = cmd.ExecuteReader())
{
while(myReader.Read())
{
string info = myReader.GetString("info");
/* Do stuff */
using(SqlConnection logConn = new SqlConnection(...))
{
logConn.Open();
string queryLog = "INSERT INTO ....;
MySqlCommand cmdLog = new MySqlCommand(queryLog, connection);
cmdLog.ExecuteNonQuery();
}
}
}
Теперь может показаться расточительным иметь два соединения, а не одно здесь. Однако:
- Объединение в пул делает создание соединений довольно дешевым.
- Мы выпускаем его обратно в пул, как только выполним
ExecuteNonQuery()
, поэтому, если это будет происходить во многих потоках, общее число подключений на ходу, скорее всего, будет вдвое меньше. количество потоков. С другой стороны, если это делает только один поток, тогда есть только одно дополнительное соединение, так что кого это волнует. - Закрытие соединения, когда оно находилось на полпути через предоставление устройства чтения данных, возможно, дало ему дополнительную очистку перед возвратом в пул, так что вы все равно можете использовать два соединения (я не знаю о SQLServer,но я знаю, что некоторым базам данных требуется больше работы, прежде чем они смогут вернуться в пул в таких случаях).
Таким образом, вы не можете использовать соединение без его повторного использования, этоошибкаОбычно вы не можете одновременно выполнять более одной операции с одним и тем же соединением.Использование опции MultipleActiveResultSets
с SQLServer2005 позволяет вам сделать это, хотя и имеет свои сложности.Как правило, если вы хотите выполнять более одной операции с БД одновременно, вам следует использовать более одного соединения.
При этом вы все равно можете использовать if
вместо while
, но сделайте это.потому что это то, что имеет смысл в то время, а не исправлять ошибку, которую вы не понимаете, и возвращать ее когда-нибудь, когда if
не вариант.