When you create 'using' blocks for your SQL Connection, Transaction, and Command, it is well known that the connection, transaction, or command that the using block is associated with is disposed on its own properly after you leave the using block.
If an exception occurs in one of these blocks though, for instance in the command block - Would the transaction be rolled back on its own, or do developers need to do a try catch inside of the command 'using' block, and add a rollback transaction statement in the catch for this try?
Automatic rollback happens when a query fails to execute for any reason.
ROLLBACK is the SQL command that is used for reverting changes performed by a transaction. When a ROLLBACK command is issued it reverts all the changes since last COMMIT or ROLLBACK.
If you want to rollback a transaction, you should not call the Complete method within the transaction scope. For example, you can throw an exception within the scope. The transaction in which it participates in will be rolled back.
ROLLBACK in SQL is a transactional control language that is used to undo the transactions that have not been saved in the database. The command is only been used to undo changes since the last COMMIT.
The transaction is rolled back automatically as long as you haven't successfully called Commit. So your using blocks can look something like this, and the transaction will be rolled back if an exception is thrown before the Commit.
using (IDbConnection connection = ...)
{
connection.Open();
using (IDbTransaction transaction = connection.BeginTransaction())
{
using (IDbCommand command = ...)
{
command.Connection = connection;
command.Transaction = transaction;
...
}
...
transaction.Commit();
}
}
It's not guaranteed to get disposed. The Dispose(bool)
method of the SqlTransaction
will in fact roll it 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, it would only happen if this._internalTransaction.Dispose();
got called. The problem here is that if GetBestEffortCleanupTarget
throws an exception it won't get cleaned up.
In your case, as long as an exception isn't thrown as already stated, you will fall into the category of being Zombied
and so it will then actually issue a Rollback
call in the _internalTransaction.Dispose()
call.
Finally, if this is called with false
it will most certainly not get disposed.
Now, unless I'm really missing something here I'm a bit appalled at how fragile this code is.
An interesting note is that I think the MSDN documentation is actually wrong because it states, for the Rollback()
method:
The transaction can only be rolled back from a pending state (after BeginTransaction has been called, but before Commit is called). The transaction is rolled back in the event it is disposed before Commit or Rollback is called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With