Qual è il modo corretto per garantire la chiusura di una connessione SQL quando viene generata un'eccezione?
-
02-07-2019 - |
Domanda
Uso un modello che assomiglia spesso a questo. Mi chiedo se va bene o se esiste una buona pratica che non sto applicando qui.
Nello specifico mi chiedo; nel caso in cui venga generata un'eccezione, il codice che ho nel blocco finalmente è abbastanza per garantire che la connessione sia chiusa in modo appropriato?
public class SomeDataClass : IDisposable
{
private SqlConnection _conn;
//constructors and methods
private DoSomethingWithTheSqlConnection()
{
//some code excluded for brevity
try
{
using (SqlCommand cmd = new SqlCommand(SqlQuery.CountSomething, _SqlConnection))
{
_SqlConnection.Open();
countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
}
}
finally
{
//is this the best way?
if (_SqlConnection.State == ConnectionState.Closed)
_SqlConnection.Close();
}
//some code excluded for brevity
}
public Dispose()
{
_conn.Dispose();
}
}
Soluzione
Riporta il codice di gestione del tuo database in un " usando "
using (SqlConnection conn = new SqlConnection (...))
{
// Whatever happens in here, the connection is
// disposed of (closed) at the end.
}
Altri suggerimenti
Il .Net Framework gestisce un pool di connessioni per un motivo. Fidati! :) Non devi scrivere così tanto codice solo per connetterti al database e rilasciare la connessione.
Puoi semplicemente usare l'istruzione "using" e stare certo che "IDBConnection.Release ()" chiuderà la connessione per te.
Le "soluzioni" altamente elaborate tendono a generare codice errato. Semplice è meglio.
Documenti MSDN rendono questo abbastanza chiaro ...
- Il metodo Chiudi ripristina tutte le transazioni in sospeso. Rilascia quindi la connessione al pool di connessioni o chiude la connessione se il pool di connessioni è disabilitato.
Probabilmente non hai (e non vuoi) disabilitare il pool di connessioni, quindi alla fine il pool gestisce lo stato della connessione dopo aver chiamato " Close " ;. Questo potrebbe essere importante poiché potresti essere confuso guardando dal lato del server database tutte le connessioni aperte.
- Un'applicazione può chiamare Chiudi più di una volta. Non viene generata alcuna eccezione.
Quindi perché preoccuparsi di testare per Closed? Basta chiamare Close ().
- Close e Dispose sono funzionalmente equivalenti.
Questo è il motivo per cui un blocco usando provoca una connessione chiusa. utilizzando chiama Dispose per te.
- Non chiamare Close o Dispose su una connessione, un DataReader o qualsiasi altro oggetto gestito nel metodo Finalize della tua classe.
Importante consiglio di sicurezza. Grazie, Egon.
Suppongo che da " _SqlConnection.State == ConnectionState.Closed " intendevi! =.
Questo funzionerà sicuramente. Penso che sia più consuetudine contenere l'oggetto connessione stesso all'interno di un'istruzione using, ma quello che hai è buono se vuoi riutilizzare lo stesso oggetto connessione per qualche motivo.
Una cosa che dovresti assolutamente cambiare, però, è il metodo Dispose (). Non è necessario fare riferimento all'oggetto connessione in dispose, poiché potrebbe essere già stato finalizzato a quel punto. Dovresti invece seguire il modello Dispose raccomandato.
Dato che stai usando IDisposables comunque. Puoi usare la parola chiave "using", che equivale sostanzialmente a chiamare dispose in un blocco finally, ma ha un aspetto migliore.
Vedi questa domanda per la risposta:
Chiudi e elimina - quale chiamare?
Se la durata della connessione è una singola chiamata di metodo, utilizzare la funzione using
della lingua per garantire la corretta pulizia della connessione. Mentre un blocco try / finally
è funzionalmente lo stesso, richiede più codice e IMO è meno leggibile. Non è necessario controllare lo stato della connessione, puoi chiamare Dispose
indipendentemente e gestirà la pulizia della connessione.
Se la durata della connessione corrisponde alla durata di una classe contenente, implementare IDisposable
e ripulire la connessione in Dispose
.
Inserisci il codice di chiusura della connessione in un " Infine " bloccare come si mostra. Infine, i blocchi vengono eseguiti prima che venga generata l'eccezione. Utilizzando un " usando " il blocco funziona altrettanto bene, ma trovo l'esplicito " Infine " metodo più chiaro.
L'uso delle dichiarazioni è un cappello vecchio per molti sviluppatori, ma gli sviluppatori più giovani potrebbero non saperlo a mano.
non c'è bisogno di provare..finalmente intorno a " usando " ;, l'uso È un try..finally
Potrei suggerire questo:
class SqlOpener : IDisposable
{
SqlConnection _connection;
public SqlOpener(SqlConnection connection)
{
_connection = connection;
_connection.Open();
}
void IDisposable.Dispose()
{
_connection.Close();
}
}
public class SomeDataClass : IDisposable
{
private SqlConnection _conn;
//constructors and methods
private void DoSomethingWithTheSqlConnection()
{
//some code excluded for brevity
using (SqlCommand cmd = new SqlCommand("some sql query", _conn))
using(new SqlOpener(_conn))
{
int countOfSomething = Convert.ToInt32(cmd.ExecuteScalar());
}
//some code excluded for brevity
}
public void Dispose()
{
_conn.Dispose();
}
}
Spero che aiuti :)