2012-03-01 15 views
5

¿Debo llamar a dispose en el bloque finally para SqlTransaction? Pretenda que el desarrollador no usó USANDO en ninguna parte, y solo intente/atrape.¿Es necesario que SqlTransaction tenga Dispose llamado?

SqlTransaction sqlTrans = con.BeginTransaction(); 

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

      sqlTrans.Rollback(); 
     } 

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

no hace daño tenerlo. – Brian

+1

Si no está utilizando un 'using' alrededor de' sqlTrans' entonces ciertamente no hace daño llamar explícitamente a 'Dispose()' en él. –

+0

@Cory ¿Es necesario comprobar si sqlTrans ya está dispuesto antes de llamar a sqlTrans.Dispose()? – Lijo

Respuesta

9

¿Es necesario utilizar try-finally o la using -statement disponer el SqlTransaction?

No hace daño tenerlo. Esto es cierto para cada clase que implementa IDisposable, de lo contrario no implementaría esta interfaz.

Pero normalmente el recolector de basura trataría con él si el objeto ya no se referencia. Como tampoco quiero llamar al dispose en cada segunda variable ni utilizar el using-statement en todas partes, siempre vale la pena examinar la implementación real del método de clase 'Dispose'.

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); 
} 

Sin la comprensión de todos (o nada) lo que está sucediendo aquí puedo decir que esto es más que un simple base.Dispose(disposing). Por lo tanto, podría ser una buena idea asegurarse de que se elimine una SqlTransaction.

Pero debido SqlConnection.BeginTransaction crea la transacción también podría ser una buena idea para reflect esto también:

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; 
} 

Como se puede ver. El GC también mantendrá la conexión activa cuando se crea una transacción. Tampoco contiene una referencia a la transacción ya que solo la devuelve. Por lo tanto, podría no eliminarse incluso cuando la conexión ya esté dispuesta. Otro argumento para deshacerse de la transacción.

También puede consultar el TransactionScope class que es más a prueba de fallas que BeginTransaction. Eche un vistazo a this question para obtener más información.

+0

También es posible que desee ver BeginSqlTransaction() y el código Connection.Dispose(). SqlTransaction parece ser un wrpapper alrededor de su InternalTransaction que se borra en Connection.Dispose(). –

+1

Respetuosamente no estoy de acuerdo con el comentario 'Esto es cierto para todas las clases que implementan IDisposable'. ¿Qué pasa con el control TableCell y otros controles HTML? Se aconseja NO aplicar 'usar' sobre ellos. – Lijo

+1

@Lijo: Punto tomado, eso fue demasiado general. Solo necesita anular la implementación o usar la declaración 'using' si necesita disponer de algunos recursos no administrados, como una conexión de base de datos. Un control implementa 'IDposable' porque el control (es decir, un' UserControl') podría contener recursos no administrados y se eliminará al final del ciclo de vida (página recursivamente en todos los controles secundarios). –

7

En el caso general, todos los IDisposable objeto a construir u obtener, y el propietario, tiene que desechar.

En el caso específico, hay algunas excepciones, pero SqlTransaction no es una de ellas.

Según el the documentation of SqlTransaction.Dispose:

Libera los recursos no administrados utilizados por el DbTransaction y libera opcionalmente, los recursos administrados.

(el subrayado es mío)

Dado que la documentación no indica que esos recursos no administrados que se liberan cuando se emite una confirmación o una retrotracción, tendrá que deshacerse de este objeto.

0

Creo que puede cerrar la conexión. Revisé el código de BCL, parece que las conexiones se encargan de su transacción, no es necesario cerrarlo explícitamente.

Cuestiones relacionadas