If you are 100% sure all your connections, commands, transactions, etc. are getting gracefully disposed, then you might want to check how you are instantiating your transactions.
The TransactionScope default constructor was incorrectly designed by Microsoft and sooner or later it causes locks (similar issues could apply to other transaction classes). The default constructor utilize the "Serializable isolation level" which is prone to cause locks.
The issue is even worse: Should you instantiate an TransactionScope with a better isolation level (for example, ReadCommitted), in the event that your SQL operation fails and performs a rollback, it will internally change the isolation level again to "Serializable", which will be prone to locks to any other command using the same transaction instance.
This was reported as a bug to Microsoft, and MS simply replied with the typical "Well, if this is a bug, we can't fix it because it could affect systems out there, and why do you even create TransactionScope instances using the default constructor? You shouldn't do that ever" excuse.
The only safe-way to use then the TransactionScope class, is to ALWAYS AND EACH TIME instantiate it using explicitly the desired isolation level that doesn't cause locks (the usual scenario is using ReadCommitted, but you could use others).
You can either use a static builder as proposed in here http://blogs.msdn.com/b/dbrowne/archive/2010/06/03/using-new-transactionscope-considered-harmful.aspx?Redirected=true :
public class TransactionUtils {
public static TransactionScope CreateTransactionScope()
{
var transactionOptions = new TransactionOptions();
transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
transactionOptions.Timeout = TransactionManager.MaximumTimeout;
return new TransactionScope(TransactionScopeOption.Required, transactionOptions);
}
}
Or using a custom wrapper class:
namespace System.Transactions {
/**************************************************************************************************************
* IMPORTANT: This class must ALWAYS be used instead of the regular "TransactionScope" class. This class
* enforces having transactions that read "only committed" data.
*
* This is because the implementation of TransactionScope is faulty (wrong design) and it gives issues because
* it changes the connections available at the the connection pool. To read more, check this link:
* http://blogs.msdn.com/b/dbrowne/archive/2010/05/21/using-new-transactionscope-considered-harmful.aspx
*
* The namespace was set to "System.Transactions" in order to provide ease of use when updating legacy code
* that was using the old class
**************************************************************************************************************/
public class SafeTransactionScope : IDisposable {
private TransactionScope _transactionScope;
private bool _disposed;
#region Constructors
public SafeTransactionScope()
: this(TransactionManager.MaximumTimeout) {
}
public SafeTransactionScope(TimeSpan scopeTimeout)
: this(TransactionScopeOption.Required, scopeTimeout) {
}
public SafeTransactionScope(TransactionScopeOption scopeOption)
: this(scopeOption, TransactionManager.MaximumTimeout) {
}
public SafeTransactionScope(TransactionScopeOption scopeOption, TimeSpan scopeTimeout) {
this._disposed = false;
this._transactionScope = CreateTransactionScope(scopeOption, scopeTimeout);
}
#endregion
#region Disposing methods
public void Dispose() {
Dispose(true);
// Use SupressFinalize in case a subclass
// of this type implements a finalizer.
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) {
if(!this._disposed) {
if(disposing) {
if(this._transactionScope != null) {
this._transactionScope.Dispose();
this._transactionScope = null;
}
}
// Indicate that the instance has been disposed.
this._disposed = true;
}
}
#endregion
public void Complete() {
if(this._disposed) {
throw new ObjectDisposedException("SafeTransactionScope");
}
if(this._transactionScope == null) {
// This should never happen
throw new ObjectDisposedException("SafeTransactionScope._transactionScope");
}
this._transactionScope.Complete();
}
private static TransactionScope CreateTransactionScope(TransactionScopeOption scopeOption, TimeSpan scopeTimeout) {
var transactionOptions = new TransactionOptions() {
IsolationLevel = IsolationLevel.ReadCommitted,
Timeout = scopeTimeout
};
return new TransactionScope(scopeOption, transactionOptions);
}
}
}