2012-03-01 13 views
5

Devo chiamare dispose nel blocco finally per SqlTransaction? Fai finta che lo sviluppatore non abbia usato USING ovunque, e solo prova/cattura.È necessario che SqlTransaction abbia chiamato Dispose?

SqlTransaction sqlTrans = con.BeginTransaction(); 

try 
{ 
    //Do Work 
sqlTrans.Commit() 
} 
catch (Exception ex) 
     { 

      sqlTrans.Rollback(); 
     } 

finally 
     { 
      sqlTrans.Dispose(); 
      con.Dispose(); 
     } 
+0

non fa male ad averlo. – Brian

+1

Se non si sta usando un 'using' su' sqlTrans', allora certamente non fa male chiamare esplicitamente 'Dispose()'. –

+0

@Cory E 'necessario verificare se sqlTrans è già eliminato prima di chiamare sqlTrans.Dispose()? – Lijo

risposta

9

Ho bisogno di usare try-finally o using -affermazione di smaltire il SqlTransaction?

Non fa male averlo. Questo è vero per ogni classe che implementa IDisposable, altrimenti non implementerebbe questa interfaccia.

Ma normalmente il garbage collector si occuperà di esso se l'oggetto non viene più referenziato. Perché anche io non voglio chiamare dispose su ogni seconda variabile o utilizzare il using-statement ovunque, vale sempre la pena di esaminare l'effettiva implementazione del metodo 'Dispose della classe'.

SqlTransaction.Dispose:

protected override void Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     SNIHandle target = null; 
     RuntimeHelpers.PrepareConstrainedRegions(); 
     try 
     { 
      target = SqlInternalConnection.GetBestEffortCleanupTarget(this._connection); 
      if (!this.IsZombied && !this.IsYukonPartialZombie) 
      { 
       this._internalTransaction.Dispose(); 
      } 
     } 
     catch (OutOfMemoryException e) 
     { 
      this._connection.Abort(e); 
      throw; 
     } 
     catch (StackOverflowException e2) 
     { 
      this._connection.Abort(e2); 
      throw; 
     } 
     catch (ThreadAbortException e3) 
     { 
      this._connection.Abort(e3); 
      SqlInternalConnection.BestEffortCleanup(target); 
      throw; 
     } 
    } 
    base.Dispose(disposing); 
} 

Senza capire tutto (o niente) ciò che sta accadendo qui posso dire che questo è più di un semplice base.Dispose(disposing). Quindi potrebbe essere una buona idea assicurarsi che un SqlTransaction venga eliminato.

Ma perché SqlConnection.BeginTransaction crea la transazione potrebbe anche essere una buona idea per reflect anche questo:

public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) 
{ 
    SqlStatistics statistics = null; 
    string a = ADP.IsEmpty(transactionName) ? "None" : transactionName; 
    IntPtr intPtr; 
    Bid.ScopeEnter(out intPtr, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", this.ObjectID, (int)iso, a); 
    SqlTransaction result; 
    try 
    { 
     statistics = SqlStatistics.StartTimer(this.Statistics); 
     SqlTransaction sqlTransaction = this.GetOpenConnection().BeginSqlTransaction(iso, transactionName); 
     GC.KeepAlive(this); 
     result = sqlTransaction; 
    } 
    finally 
    { 
     Bid.ScopeLeave(ref intPtr); 
     SqlStatistics.StopTimer(statistics); 
    } 
    return result; 
} 

Come si può vedere. Lo GC manterrà la connessione attiva anche quando viene creata una transazione. Inoltre, non tiene un riferimento alla transazione poiché la restituisce solo. Quindi potrebbe non essere disposto anche quando la connessione è già disponibile. Un altro argomento per disporre la transazione.

Si potrebbe anche dare un'occhiata al TransactionScope class che è più sicuro rispetto a BeginTransaction. Date un'occhiata a this question per maggiori informazioni.

+0

Si potrebbe anche voler guardare BeginSqlTransaction() e il codice Connection.Dispose(). SqlTransaction sembra essere un wrpapper attorno al suo InternalTransaction che viene cancellato in Connection.Dispose(). –

+1

Non sono rispettosamente in disaccordo con il commento "Questo è vero per ogni classe che implementa IDisposable". Che dire del controllo TableCell e di altri controlli HTML. Si consiglia di NON applicare "l'uso" su di essi. – Lijo

+1

@Lijo: punto preso, era troppo generico. Devi solo eseguire l'override dell'implementazione o utilizzare l'istruzione 'using' se hai bisogno di disporre di risorse non gestite come una connessione al database. Un controllo implementa 'IDposable' perché il controllo (ad esempio un' UserControl') potrebbe contenere risorse non gestite e verrà eliminato alla fine del ciclo di vita (pagina in modo ricorsivo in tutti i controlli figlio). –

7

Nel caso generale, ogni IDisposable oggetto di costruire o di ottenere, e proprietario, è necessario disporre di.

Nel caso specifico, ci sono alcune eccezioni, ma SqlTransaction non è uno di questi.

Come per il the documentation of SqlTransaction.Dispose:

Rilascia le risorse non gestite utilizzati dal DbTransaction ed eventualmente rilascia le risorse gestite.

(corsivo mio)

Dato che la documentazione non afferma che tali risorse non gestite vengono rilasciate quando si emette un commit o un rollback, si avrà bisogno di disporre di questo oggetto.

0

Penso che si possa semplicemente chiudere la connessione. Ho controllato il codice di BCL - sembra che le connessioni si occupino della sua transazione - non c'è bisogno di chiuderla esplicitamente.