views:

619

answers:

2

I'm trying to use System.Transaction.TransactionScope to create a transaction to call a few stored procedures but it doesn't seem to clean up after itself. Once the transaction is finished (commited or not and the transaction scope object is disposed) subsequent connections to the database open up with the read commit level of serializable instead of read commited like they normally would.

I'm opening and closing a connection for each call (well closing and returning to a pool of connections like normal in .NET), am I missing some way to explicitly reset the connection when I'm done using it for a transaction? I thought the idea behind System.Transaction.TransactionScope was to hide all the complexity.

So the code I have looks like this:

      using (var scope = new TransactionScope())
      {
       ... make my 3 stored procedure calls ...

       scope.Complete();

       return returnCode;
      }

which I guess is the normal way to do it. But then if I look in sqlserver profiler I can see connections being opened with

set transaction isolation level serializable

which is messing with subsequent non-transaction related database activity and also is apparently not as fast. I can get around this by setting a transaction option to explicity do the transaction with ReadCommited but this is not the ideal behaviour for this operation in my opinion.

I've also tried explicitly creating a Commitabletransaction object, creating explict new transactions instead of using the ambient one and still no luck.

Any ideas on how to fix this would be much appreciated as any calls that use the serializable connection will throw an error if they try to use a readpast locking hint.

A: 

You should also see a reset (sp_reset_connection) between uses of the same connection in the pool; will that not reset the isolation level? Have you tried reproducing a serializable issue (for example, lock escalation deadlocks)

Marc Gravell
I definitely see the sp_reset connection. I can step through and see each connection explicitly being closed. How do you mean reproducing a serializables issue? I'm the only using the system when I'm trying this out and am not having any deadlocks (checking trace on profiler and transaction logging)
marshall
What I mean is: you claim it interferes with "subsequent non-transaction related database activity"; I'm saying: "are you sure?". I suspect the sp_reset_connection means that subsequent db activity is unaffected by this isolation level.
Marc Gravell
To check for sure, you would need to check whether the additional locks are taken. One option is a lock escalation scenario - i.e. spid A reads a row (without UPDLOCK), spid B reads a row, A tries to update the row, B tries to update the row. If this deadlocks, the spids are serializable.
Marc Gravell
If I look at the sql profiler trace it looks like this:(spid, command)65 exec sp_reset_connection65 set transaction isolation level serializable 65 exec spc_payment_ledger_get @payment_ledger_id=33,@member_id=3at which point the transaction commits.
marshall
now i view a page on the site. it still uses the same spid and isolation level even tho the transaction has completed. maybe the transaction isn't being completed but it's definitely calling the commit unless an exception is thrown in which case it should automatically roll back correct?
marshall
If the "set" is *after* the reset, then it will indeed be serializable - is spc_payment_lerger_get *outside* of a TransactionScope? Curious. Re-using the spid is expected with connection pools, though.
Marc Gravell
It's definitely outside the transaction scope. What I posted in the question is the only transaction in the system. But anything else subsequently called will fail if it has a conflicting locking hint. any db call at all, from different sessions etc.
marshall
+1  A: 

Use TransactionOptions.IsolationLevel

By default, it's serializable

TransactionOptions transactionoptions1 = new TransactionOptions();
transactionoptions1.IsolationLevel = IsolationLevel.ReadCommitted;
using (var scope = new TransactionScope(TransactionScopeOption.Required, transactionoptions1))
{
    ... make my 3 stored procedure calls ...

    scope.Complete();

    return returnCode;
}
gbn
This is the current solution that I've used to work around the problem. But I'm interested in why I'm seeing the behaviour I'm seeing. Surely subsequent db calls shouldn't use the isolation level of the transaction regardless?
marshall