TransactionScope Prematurely Completed

C#Sql Serverado.netDeadlockTransactionscope

C# Problem Overview


I have a block of code that runs within a TransactionScope and within this block of code I make several calls to the DB. Selects, Updates, Creates, and Deletes, the whole gamut. When I execute my delete I execute it using an extension method of the SqlCommand that will automatically resubmit the query if it deadlocks as this query could potentially hit a deadlock.

I believe the problem occurs when a deadlock is hit and the function tries to resubmit the query. This is the error I receive:

>The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements.

This is the simple code that executes the query (all of the code below executes within the using of the TransactionScope):

using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App))
{
	sqlCommand.Connection.Open();
	sqlCommand.ExecuteNonQueryWithDeadlockHandling();
}

Here is the extension method that resubmits the deadlocked query:

public static class SqlCommandExtender
{
	private const int DEADLOCK_ERROR = 1205;
	private const int MAXIMUM_DEADLOCK_RETRIES = 5;
	private const int SLEEP_INCREMENT = 100;

	public static void ExecuteNonQueryWithDeadlockHandling(this SqlCommand sqlCommand)
	{
		int count = 0;
		SqlException deadlockException = null;
	
		do
		{
			if (count > 0) Thread.Sleep(count * SLEEP_INCREMENT);
			deadlockException = ExecuteNonQuery(sqlCommand);
			count++;
		}
		while (deadlockException != null && count < MAXIMUM_DEADLOCK_RETRIES);

		if (deadlockException != null) throw deadlockException;
	}

	private static SqlException ExecuteNonQuery(SqlCommand sqlCommand)
	{
		try
		{
			sqlCommand.ExecuteNonQuery();
		}
		catch (SqlException exception)
		{
			if (exception.Number == DEADLOCK_ERROR) return exception;
			throw;
		}

		return null;
	}
}

The error occurs on the line:

sqlCommand.ExecuteNonQuery();

C# Solutions


Solution 1 - C#

Don't forget to supress your select statements from your TransactionScope. In SQL Server 2005 and above, even when you use with(nolock), locks are still created on those tables the select touches. Check this out, it shows you how to setup and use TransactionScope.

using(TransactionScope ts = new TransactionScope 
{ 
  // db calls here are in the transaction 
  using(TransactionScope tsSuppressed = new TransactionScope (TransactionScopeOption.Suppress)) 
  { 
    // all db calls here are now not in the transaction 
  } 
} 

Solution 2 - C#

I've found that this message can occur when a transaction runs for a longer period than the maxTimeout for System.Transactions. It doesn't matter that TransactionOptions.Timeout is increased, it can't exceed maxTimeout.

The default value of maxTimeout is set to 10 minutes and its value can only be modified in the machine.config

Add the following (in the configuration level) to the machine.config to modify the timeout:

<configuration>
    <system.transactions>
        <machineSettings maxTimeout="00:30:00" />
    </system.transactions>
</configuration>

The machine.config can be found at: %windir%\Microsoft.NET\Framework\[version]\config\machine.config

You can read more about it in this blog post: http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html

Solution 3 - C#

I can reproduce the problem. It is a transaction timeout.

using (new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 0, 1)))
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var sqlCommand = connection.CreateCommand())
        {
            for (int i = 0; i < 10000; i++)
            {
                sqlCommand.CommandText = "select * from actor";
                using (var sqlDataReader = sqlCommand.ExecuteReader())
                {
                    while (sqlDataReader.Read())
                    {
                    }
                }
            }
        }
    }
}

Throws System.InvalidOperationException with this message: >The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements.

To solve the problem make your query run faster or increase the timeout.

Solution 4 - C#

If an exception happens inside a TransactionScope it is rolled back. This means that TransactionScope is done. You must now call dispose() on it and start a new transaction. I'm honestly not sure if you can reuse the old TransactionScope or not, I've never tried, but I'd assume not.

Solution 5 - C#

My issue was a stupid one, if you sit on a debug break through the timeout you will get this. Face Palm

Man, programming makes you feel thick some days...

Solution 6 - C#

Confirmed this error can also be caused by a transaction timeout. Just to add to what Marcus + Rolf have stated, if you haven't explicitly set a timeout on the TransactionScope, the timeout TimeSpan will assume a default value. This default value is the smaller of:

  1. If you've overridden the local app.config / web.config setting, e.g.

    <system.transactions>
    <defaultSettings timeout="00:05:00" />
    </system.transactions>
    
  2. But this is then 'capped' at the machine.config setting <machineSettings maxTimeout="00:10:00" />

Solution 7 - C#

This exception can also be caused by disable Microsoft Distributed Transaction Coordinator.

If we want enable it, we run "dcomcnfg" and select "Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC" and choose "Properties".

It should be checked "Allow Remote Client", "Allow Inbound", "Allow Outbound" and "No Authentication Required".

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHungry BeastView Question on Stackoverflow
Solution 1 - C#DanView Answer on Stackoverflow
Solution 2 - C#MarcusView Answer on Stackoverflow
Solution 3 - C#RolfView Answer on Stackoverflow
Solution 4 - C#DonnieView Answer on Stackoverflow
Solution 5 - C#Samuel FlemingView Answer on Stackoverflow
Solution 6 - C#StuartLCView Answer on Stackoverflow
Solution 7 - C#Adrian TarnowskiView Answer on Stackoverflow