TransactionScope and Isolation Level

Anton Kalcik picture Anton Kalcik · Apr 6, 2009 · Viewed 19.6k times · Source

we have a problem to use TransactionScope. TransactionScope get to us very good flexibility to use transactions across our Data Access Layer. On this way we can use transactions implicit or explicit. There are some performance boost again ADO.NET transactions, but at this time this is not really problem. However we have problem with locking. In example code below, although isolation level is set to ReadCommitted, it is not possible to make Select SQL statement from other client on table testTable, until the main transaction (in Main method) will be committed, because there is lock on whole table. We also tried to use only one connection across all methods, but same behavior. Our DBMS is SQL Server 2008. Is there something what we didn't understood?

Regards Anton Kalcik

See this sample code:

class Program
{
    public class DAL
    {
        private const string _connectionString = @"Data Source=localhost\fsdf;Initial Catalog=fasdfsa;Integrated Security=SSPI;";

        private const string inserttStr = @"INSERT INTO dbo.testTable (test) VALUES(@test);";

        /// <summary>
        /// Execute command on DBMS.
        /// </summary>
        /// <param name="command">Command to execute.</param>
        private void ExecuteNonQuery(IDbCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("Parameter 'command' can't be null!");

            using (IDbConnection connection = new SqlConnection(_connectionString))
            {
                command.Connection = connection;
                connection.Open();
                command.ExecuteNonQuery();
            }
        }

        public void FirstMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello1"));

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }

        public void SecondMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello2"));

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }
    }

    static void Main(string[] args)
    {

        DAL dal = new DAL();
        TransactionOptions tso = new TransactionOptions();
        tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

        using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required,tso))
        {
            dal.FirstMethod();
            dal.SecondMethod();
            sc.Complete();
        }
    }
}

Answer

Daniel Pratt picture Daniel Pratt · Apr 6, 2009

I don't think your issue has anything to do with the .NET TransactionScope concept. Rather, it sounds like you're describing the expected behavior of SQL Server transactions. Also, changing the isolation level only affects "data reads" not "data writes". From SQL Server BOL:

"Choosing a transaction isolation level does not affect the locks acquired to protect data modifications. A transaction always gets an exclusive lock on any data it modifies, and holds that lock until the transaction completes, regardless of the isolation level set for that transaction. For read operations, transaction isolation levels primarily define the level of protection from the effects of modifications made by other transactions."

What that means is that you can prevent the blocking behavior by changing the isolation level for the client issuing the SELECT statement(s). The READ COMMITED isolation level (the default) won't prevent blocking. To prevent blocking the client, you would use the READ UNCOMMITTED isolation level, but you would have to account for the possibility that records may be retrieved that have been updated/inserted by an open transaction (i.e. they might go away if the transaction rolls back).