9

SqlTransaction の finally ブロックで dispose を呼び出す必要がありますか? 開発者がどこでも USING を使用しなかったふりをして、ただ try/catch します。

SqlTransaction sqlTrans = con.BeginTransaction();

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

           sqlTrans.Rollback();
        }

 finally
        {
            sqlTrans.Dispose();
            con.Dispose();
        }
4

3 に答える 3

19

を破棄するにはtry-finally、またはステートメントを使用する必要がありますか?usingSqlTransaction

持っていて損はありません。これは、 IDisposableを実装するすべてのクラスに当てはまります。それ以外の場合、このインターフェイスは実装されません。

ただし、通常、ガベージ コレクターは参照されていないオブジェクトを処理します (GC が dispose を呼び出すという意味ではありませんが、これは true ではありません)。したがって、管理されていないリソースに対してのみ必要です。disposeしかし、他のすべての変数を呼び出したり、どこでもusing ステートメントを使用したりしたくないため、クラスの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、トランザクションを作成するため、これも反映することをお勧めします。

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は接続を維持します。また、トランザクションを返すだけなので、トランザクションへの参照も保持しません。したがって、接続がすでに破棄されている場合でも、破棄されない場合があります。トランザクションを破棄する別の引数。

よりもフェイルセーフなTransactionScopeクラスを見ているかもしれませんBeginTransaction。詳細については、この質問をご覧ください。

于 2012-03-01T23:18:23.683 に答える
8

一般的に、作成または取得して所有するすべてのIDisposableオブジェクトは、破棄する必要があります。

特定のケースでは、いくつかの例外がありますがSqlTransaction、それらの 1 つではありません。

のドキュメントSqlTransaction.Disposeに従って:

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

(私の強調)

ドキュメントには、コミットまたはロールバックを発行したときにこれらの管理されていないリソースが解放されるとは記載されていないため、このオブジェクトを破棄する必要があります。

于 2012-03-01T23:12:22.517 に答える
1

接続を閉じるだけでよいと思います。BCL のコードを確認しました - 接続がそのトランザクションを処理しているようです - 明示的に閉じる必要はありません。

于 2012-03-01T23:16:42.577 に答える