2012-03-01 50 views
5

SqlTransactionのfinallyブロックでdisposeを呼び出す必要がありますか?開発者がどこでUSINGを使用していないかのふりをして、/ catchを試してみてください。一般的なケースではSqlTransactionはDisposeを呼び出す必要がありますか?

SqlTransaction sqlTrans = con.BeginTransaction(); 

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

      sqlTrans.Rollback(); 
     } 

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

それを持つことを傷つけることはありません。 – Brian

+1

'sqlTrans'の周りに' using'を使用していない場合、明示的に 'Dispose()'を呼び出すことに傷つくことはありません。 –

+0

@Cory sqlTrans.Dispose()を呼び出す前にsqlTransがすでに破棄されているかどうかを確認する必要がありますか? – Lijo

答えて

9

私はSqlTransactionを配置するtry-finallyまたはusing -statementを使用する必要がありますか?

それは傷ついていません。これはIDisposableを実装しているすべてのクラスに当てはまります。そうでなければ、このインタフェースを実装しません。

通常、ガベージコレクタは、オブジェクトがもはや参照されていない場合はそれを処理します。すべての第2変数にdisposeを呼び出したり、どこでもusing-statementを使用したりしたくないので、クラス '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); 
} 

すべての(または何かを)理解せずに、ここで何が起こっているのか私は、これは、単純なbase.Dispose(disposing)以上であると言うことができます。ですから、SqlTransactionが確実に破棄されるようにすることは良い考えです。

しかしSqlConnection.BeginTransactionは、トランザクションを作成しているため、またreflectまた、これには良いアイデアかもしれない:

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

あなたが見ることができるように。 GCは、トランザクションの作成時にもConnectionを有効に保ちます。それはそれが返すだけなので、トランザクションへの参照も保持しません。そのため、接続が既に行われていても、それを廃棄することはできません。トランザクションを破棄する別の引数。

さらに、BeginTransactionよりもフェイルセーフであるTransactionScope classを調べることもできます。詳細については、this questionをご覧ください。

+0

また、BeginSqlTransaction()とConnection.Dispose()コードです。 SqlTransactionはConnection.Dispose()でクリアされるInternalTransactionの周りのwrpapperのようです。 –

+1

私は「これはIDisposableを実装しているすべてのクラスで当てはまります」というコメントには正直に同意しません。 TableCellコントロールと他のHTMLコントロールはどうでしょうか?それらに「using」を適用しないことをお勧めします。 – Lijo

+1

@Lijo:ポイントを取った、それはあまりにも一般的だった。実装をオーバーライドするか、データベース接続のようないくつかのアンマネージリソースを処分する必要がある場合は、 'using'ステートメントを使用するだけです。コントロールは、管理されていないリソースを含む可能性があり、ライフサイクルの終わりに(再帰的にすべての子コントロールに)配置されるため、 'IDposable'を実装しています。 –

7

、あなたが構築または取得、および所有あらゆるIDisposableオブジェクトは、あなたはを処分しなければなりません。

特定のケースでは、いくつかの例外がありますが、SqlTransactionはその1つではありません。 the documentation of SqlTransaction.Dispose 1として

リリースDbTransactionによって使用されるアンマネージリソースし、オプションでマネージリソースを解放します。

(私の強調)

ドキュメントは、これらのアンマネージリソースを使用すると、コミットまたはロールバックを発行したとき、あなたはこのオブジェクトを処分する必要があります解放された状態はありませんので。

0

接続を閉じることができると思います。私はBCLのコードをチェックしました - 接続のようなトランザクションを処理するように思える - それを明示的に閉じる必要はありません。

関連する問題