È possibile infatti utilizzare if
, ma si dovrebbe anche essere in grado di utilizzare while
in un caso come questo senza un errore. Basta cambiarlo in if
, ma se si fa la stessa cosa in un caso in cui è possibile avere più di una riga, si otterrà lo stesso errore. È quindi importante esaminare il motivo per cui l'eccezione stava accadendo.
Esaminiamo il codice:
while (myReader.Read())
{
info = myReader.GetString("info");
...
Fin qui tutto bene.
/**
* 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!!!!
Oh uh! Hai risolto i tuoi "problemi", ma sapevi quali erano quei "problemi"? In realtà è qui che hai rotto il tuo while
.
Al punto in cui ho detto "Fin qui tutto bene", questo è ciò che avete succedendo:
- si ha un oggetto di connessione. È aperto".
- Si dispone di un oggetto lettore di dati. Sta recuperando dati dalla connessione.
Ora, dalle specifiche di IDbCommand.ExecuteReader
(http://msdn.microsoft.com/en-us/library/68etdec0.aspx), il collegamento è in uso per quel lettore. Non ti è permesso fare nulla con quella connessione eccetto chiuderla, finché non chiami Close()
sul lettore, esplicitamente o tramite Dispose()
come accadrebbe se il lettore fosse assegnato in un blocco using
.
Ora, rigorosamente questo non è sempre vero. Una determinata implementazione è sempre consentita a fornire più di un'interfaccia promessa (indipendentemente dalla firma dell'interfaccia o dalla documentazione). Molte implementazioni "rilasceranno" la connessione per il riutilizzo una volta che Read()
restituisce false (l'unica che abbia mai implementato), SQLServer 2005 ha un'opzione MultipleActiveResultSets=True
che consente alla stessa connessione di supportare simultaneamente più datareader contemporaneamente (ci sono aspetti negativi). Tuttavia, a parte queste distorsioni delle regole, l'unica cosa che possiamo assolutamente fare con la nostra connessione in questo momento è chiamare il numero Close()
.
Per questo motivo, quando si è tentato di chiamare cmdLog.ExecuteNonQuery()
si sono verificati "alcuni problemi" sotto forma di un messaggio di errore, perché si stava tentando di utilizzare una connessione in uso per qualcos'altro.
Quindi, "l'hai risolto" facendo l'unica cosa che potevi fare con la connessione - chiudendola - e poi aprendola di nuovo. A tutti gli effetti, avresti una nuova connessione!
Ora, quando si torna al while
, si chiama di nuovo myReader.Read()
.Questo metodo legge dal flusso di dati provenienti dalla connessione e restituisce false (se non ci sono altri risultati) o crea un set di campi in base a ciò che legge da quel flusso.
Ma non è così, perché hai chiuso quella connessione. Quindi "esplode" con un'eccezione perché stai cercando di leggere da una connessione chiusa e questo non può essere fatto.
Ora, quando hai sostituito questo while
con un if
non sei mai tornato all'operazione Read()
che hai interrotto, quindi il codice funziona di nuovo.
Ma cosa hai intenzione di fare se hai mai bisogno di farlo con un set di risultati che ha più di una riga?
È possibile utilizzare MARS. Vedi http://msdn.microsoft.com/en-us/library/h32h3abf%28v=vs.80%29.aspx per quello. Francamente lo eviterei a meno che qualcosa non ottenga più da questo; ha alcune implicazioni sottili che possono essere davvero confuse se le colpisci.
È possibile posticipare l'operazione successiva fino a dopo la chiusura del lettore. In questo caso, questo sarebbe solo un risparmio minore di togliere il superfluo Open()
e 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();
}
}
}
Ora, può sembrare uno spreco di avere due connessioni piuttosto che uno qui. Tuttavia:
- Il pooling rende la creazione di connessioni piuttosto economiche.
- Lo stiamo rilasciando in piscina non appena facciamo il
ExecuteNonQuery()
, quindi se c'è un sacco di questo in corso su molti thread il numero totale di connessioni in movimento sarà probabilmente inferiore al doppio del numero di filettature. D'altra parte, se c'è solo un thread che fa questo, allora c'è solo una connessione in più, quindi a chi importa.
- Chiusura di una connessione quando era parzialmente attraverso la fornitura di un datareader potrebbe aver dato una pulizia extra da fare prima di tornare al pool, quindi potresti benissimo usare due connessioni (non so di SQLServer, ma so che alcuni database richiedono più lavoro prima che possano tornare in piscina in questi casi).
In sintesi, non è possibile utilizzare una connessione senza riutilizzarla, si tratta di un bug out-and-out. Di norma non è possibile avere più di una operazione sulla stessa connessione alla volta. L'utilizzo dell'opzione MultipleActiveResultSets
con SQLServer2005 consente di farlo, sebbene abbia delle complicazioni. In genere, se si desidera eseguire più di una cosa DB alla volta, è necessario utilizzare più di una connessione.
Detto questo, è comunque possibile utilizzare if
piuttosto che while
, ma farlo perché è ciò che ha senso, al momento, piuttosto che per correggere un bug che non capisci e farlo tornare qualche tempo, quando if
isn 'un'opzione.
Che cosa significa "esplode"? – SLaks
Sembra che ci sia un problema di fondo che probabilmente dovresti capire piuttosto che evitarlo come hai fatto tu. Cosa significa esplodere? Viene lanciata un'eccezione? Che eccezione? –
È un'eccezione, non ho a portata di mano in questo momento, ma riguarda le connessioni. Ho modificato il post per aggiungere del codice a riguardo. – Kani