Is Rollback automatic in a "Using" scope with C# SQL Server calls? - c#

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?

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.

Related

OracleConnection: what are Clearpool, Dispose and Close?

I would like to understand what does Clearpool, Dispose, Close do in OracleConnection?
After exception occurs, I would like to get rid off the current connection and create a total new connection with the same connection string. How should I get rid off the old connection?
Should l clearpool firstly or dispose the connection? And what does clearpool vs dispose/close do?
My current code is like below:
public virtual void Dispose()
{
try
{
_connection.Close();
}
catch (Exception e)
{
}
finally
{
_connection.Dispose();
_connection.ClearPool();
_connection = null;
}
}
The easiest way to solve this is to create the connection inside a using block. Everything inside that block will be disposed whenever the block execution finishes. Also, do NOT leave a catch with no action inside. This might end on a silent exception that you will be not aware of.
using(OracleConnection conn = new OracleConnection("yourConnStr"))
{
//ALL YOUR LOGIC INSIDE
}

C# transaction with handled exception will still roll back?

Considering this piece of code:
using(TransactionScope tran = new TransactionScope()) {
insertStatementMethod1();
insertStatementMethod2();
// this might fail
try {
insertStatementMethod3();
} catch (Exception e) {
// nothing to do
}
tran.Complete();
}
Is anything done in insertStatementMethod1 and insertStatementMethod2 going to be rolled back? In any case?
If I want them to execute anyway, I would need to check if it insertStatementMethod3 will fail before the transaction, and build my transaction code based on that?
Update
The code looks similar to this
using(TransactionScope tran = new TransactionScope()) {
// <standard code>
yourExtraCode();
// <standard code>
tran.Complete();
}
where I get to write the yourExtraCode() method
public void yourExtraCode() {
insertStatementMethod1();
insertStatementMethod2();
// this call might fail
insertStatementMethod3();
}
I can only edit the yourExtraCode() method, so I cannot chose to be in the transaction scope or no. One simple possible solution would be this:
public void yourExtraCode() {
insertStatementMethod1();
insertStatementMethod2();
// this call might fail
if (findOutIfIcanInsert()) { // <-- this would come by executing sql query
try {
insertStatementMethod3();
} catch (Exception e) {
// nothing to do
}
}
}
But that would come with the need of looking up things in the db which would affect performance.
Is there a better way, or I need to find out before I'd call the method?
I tried out and, of course the transaction was rolled back as expected.
If you don't want your first two methods to be transacted, just move them out from the ambient transaction's scope.
If you don't have control over the code which starts an ambient transaction, you can suppress it by creating a new ambient transaction: using (var scope = new TransactionScope(TransactionScopeOption.Suppress)).

TransactionScope has aborted transaction before disposal

When using TransactionScope it apperars that if internally executed code rolled back the transaction than the parent transaction will rollback as well. Which is good for me. But when disposing that scope it throws an exception meaning that transaction was rolled back already and aborted.
So what is the right way to handle that and properly dispose the scope?
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (var conn = GetConnection())
{
string query =
#"some query that may contain transaction itself
or some SP whith transaction included"
using (var command = new SqlCommand(query, conn))
command.ExecuteNonQuery();
}
}
scope.Complete();
} // Exception here
scope.Dispose() may throw TransactionAborted exception even if scope.Complete() has been called. For example some stored procedures a smart enough to handle exceptions and abort transaction inside T-SQL script using T-SQL TRY/CATCH construct w/o throwing exception to the caller.
So I would consider the safest approach I would suggest is as follows:
try
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{
using (var conn = GetConnection())
{
string query =
#"some query that may contain transaction itself
or some SP whith transaction included"
using (var command = new SqlCommand(query, conn))
command.ExecuteNonQuery();
}
}
catch (SqlException ex)
{
// log SQL Exception, if any
throw; // re-throw exception
}
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
// we can get here even if scope.Complete() was called.
// log TransactionAborted exception if necessary
}
And don't worry about disposing TransactionScope. scope.Dispose performs whatever necessary to clean it up before throwing TransactionAborted exception.
I do not use stored procs or SQL-specific try/catch, so my situation was slightly different but I got the exact same transaction aborted exception mentioned in the post. I found that if I have a SELECT somewhere inside the primary TransactionScope, that will cause the Transaction to commit, although I am not sure why. I had a case where in order to create an object in the database it first checked to make sure the object didn't already exist with a SELECT, and then the abort exception occurred when Dispose was called. I looked at the Inner Exception and it said the Transaction was trying to commit without a begin. I finally tried wrapping my SELECT in a Suppressed TransactionScope, and then it worked. So:
using(TransactionScope tx = new TransactionScope())
{
//UPDATE command for logging that I want rolled back if CREATE fails
using(TransactionScope tx2 = new TransactionScope(TransactionScopeOption.Suppress))
{
// SELECT command
}
//If not exists logic
//CREATE command
} //Used to error here, but not with the SELECT Suppressed
I hope this helps anyone else who might have gotten this exception without using stored procs.
If an exception throws from your inner query the scope.Complete() line 'll not executed.
Please refer the link below.. And I've made some changes to your query also. I hope it should work for you.
Transaction Scope
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (var conn = GetConnection())
{
string query =
#"some query that may contain transaction itself
or some SP whith transaction included"
using (var command = new SqlCommand(query, conn))
command.ExecuteNonQuery();
}
scope.Complete();
}

Transactions commit and rollback with Entity Framework, Mysql and LINQ to SQL in C#

My problem is that the transaction is not working properly it should not save the data for one table if an exception occurs during the trascation
When all the table is correct then only save data.
Consider the following:
databaseEntites objEntites = null;
using (objEntites = new databaseEntites())
{
objEntites.Connection.Open();
using (System.Data.Common.DbTransaction transaction =
objEntites.Connection.BeginTransaction())
{
try
{
customer objcust=new customer();
objcust.id=id;
objcust.name="test1";
objcust.email="test#gmail.com";
objEntites.customer.AddObject(objcust);
order objorder=new order();
objorder.custid=objcust.id;
objorder.amount=500;
objEntites.order.AddObject(objorder);
objEntites.SaveChanges();
transaction.Commit();
}
catch()
{
transaction.Rollback();
}
}
}
In this my second table column name is not correct and on SaveChanges() giving the exception.
When i see the database and found that it saving the data for customer table which is wrong i want data will go in the customer table when all table is correct and this savechanges either save for all table or not save for any.
For this i have also try the TransactionScope
using (TransactionScope tscope =
new TransactionScope(TransactionScopeOption.RequiresNew))
{
......all the code here....
objEntites.SaveChanges(false);
tscope.Complete();
objEntites.AcceptAllChanges();
}
But its giving the same issue as described above.
Thanks in advance.
You can use database transaction or EF TransactionScope. For using database transaction it is enough to do as below:
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
//Some stuff
dbContextTransaction.Commit();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
And for using second way that EF TransactionScope just use easily as below:
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
try
{
//Some stuff
scope.Complete();
}
catch (Exception)
{
//Don't need call any rollback like method
}
}
The point is no Rollback() method exist in the TransactionScope (against the normal ADO.NET Transaction) and Unless you call the Complete() method, the transaction do not complete and all the changes are rolled back automatically. You can see MSDN for better understand: http://msdn.microsoft.com/en-us/data/dn456843.aspx
Hope this help
If you have already a try ... catch block, you don't need using - just add finally. The following example should have everything you need:
databaseEntites objEntites = null;
var System.Data.Common.DbTransaction transaction = null;
try
{
objEntites = new databaseEntites();
objEntites.Connection.Open();
transaction = objEntites.Connection.BeginTransaction();
customer objcust=new customer();
objcust.id=id;
objcust.name="test1";
objcust.email="test#gmail.com";
objEntites.customer.AddObject(objcust);
order objorder=new order();
objorder.custid=objcust.id;
objorder.amount=500;
objEntites.order.AddObject(objorder);
objEntites.SaveChanges();
transaction.Commit();
}
catch()
{
if (transaction != null) transaction.Rollback();
}
finally
{
if (objEntites != null && objEntites.Connection.State != System.Data.ConnectionState.Closed
&& objEntites.Connection.State != System.Data.ConnectionState.Broken)
objEntites.Connection.Close();
}
Hints:
The finally block is executed even after an exception has occured, hence in case of an exception the exception handling code is executed first, then the code in the finally block. Only if severe exceptions (system errors) - such as a StackOverflowException - occur, it is not executed but you can't handle such kinds of exceptions anyway easily. For more information about this topic please look here.
For SaveChanges you can also use the option System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave, which I prefer because it guarantees that every entity object has its changes accepted after successful save.
BeginTransaction allows also to specify the kind of transaction, but I would not use the parameter options because if you omit it then it creates a transaction as specified by the database's default - so the administrator is still able to change this easily if required. But if you need to specify it, ensure that you don't hardcode it, but allow to configure it in the App.Config or Web.Config file.
Please let me know if that works for you.

.net TransactionScope Exception

My Code something like this
try
{
using (TransactionScope iScope = new TransactionScope())
{
try
{
isInsertSuccess = InsertProfile(account);
}
catch (Exception ex)
{
throw;
}
if (isInsertSuccess)
{
iScope.Complete();
retValue = true;
}
}
}
catch (TransactionAbortedException tax)
{
throw;
}
catch (Exception ex)
{
throw;
}
Now what happen is that even if my value is TRUE a TransactionAbortedException Exception occurs randomly, but data get's inserted/updated in DB.
Any idea what went wrong?
As the TransactionAbortedException documentation says,
This exception is also thrown when an attempt is made
to commit the transaction and the
transaction aborts.
This is why you see the exception even after calling Transaction.Complete: the Complete method is not the same thing as Commit:
calling this method [TransactionScope.Complete] does not guarantee
a commit of the transaction. It is
merely a way of informing the
transaction manager of your status
The transaction isn't committed until you exit the using statement: see the CommittableTransaction.Commit documentation for details. At that point any actions participating in the transaction may vote to abort the transaction and you'll get a TransactionAbortedException.
To debug the underlying problem you need to analyze the exception details and stack trace. As Mark noted in a comment, it may well be caused by a deadlock or another interaction with other database processes.

Categories

Resources