This is not guaranteed. The Dispose(bool) SqlTransaction will actually roll back conditionally:
// System.Data.SqlClient.SqlTransaction 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); }
and if you notice, this will only happen if this._internalTransaction.Dispose(); is called this._internalTransaction.Dispose(); . The problem here is that if GetBestEffortCleanupTarget throws an exception, it will not be cleared.
In your case, if the exception is not thrown as already indicated, you will Zombied into the Zombied category, and then it actually issues a Rollback call in the _internalTransaction.Dispose() call.
Finally, if called with false , it will certainly not be deleted.
Now, if I really can't see anything, I'm a little shocked at how fragile this code is.
It is interesting to note that, in my opinion, the MSDN documentation is actually incorrect , as it indicates the Rollback() method:
A transaction can only be canceled from the standby state (after calling BeginTransaction, but before calling Commit). A transaction is rolled back if it is withdrawn before calling Commit or Rollback.
source share