Why is System.Transactions TransactionScope default Isolationlevel Serializable

C#TransactionscopeIsolation Level

C# Problem Overview


I am just wondering what a good reason to use Serializable as the default Isolationlevel may be when creating a System.Transactions TransactionScope, because I cannot think of any (and it seems that you cannot change the default via web/app.config so you always have to set it in your code)

using(var transaction = TransactionScope()) 
{
    ... //creates a Transaction with Serializable Level
}

Instead I always have to write boilerplate code like this:

var txOptions = new System.Transactions.TransactionOptions();
txOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
    
using(var transaction = new TransactionScope(TransactionScopeOption.Required, txOptions)) 
{
    ...
}

Any ideas?

C# Solutions


Solution 1 - C#

The fact Serializable is the default comes from times when .NET wasn't even released (before year 1999), from DTC (Distributed Transaction Coordinator) programming.

DTC uses a native ISOLATIONLEVEL enumeration:

> ISOLATIONLEVEL_SERIALIZABLE > Data read by a current transaction cannot > be changed by another transaction until the current transaction > finishes. No new data can be inserted that would affect the current > transaction. This is the safest isolation level and is the default, > but allows the lowest level of concurrency.

.NET TransactionScope is built on top of these technologies.

Now, the next question is: why DTC defines ISOLATIONLEVEL_SERIALIZABLE as the default transaction level? I suppose it's because DTC was designed around year 1995 (before 1999 for sure). At that time, the SQL Standard was SQL-92 (or SQL2).

And here is what SQL-92 says about transaction levels:

> An SQL-transaction has an isolation level that is READ UNCOMMITTED, > READ COMMITTED, REPEATABLE READ, or SERIALIZABLE. The isolation level > of an SQL-transaction defines the degree to which the operations on > SQL-data or schemas in that SQL-transaction are affected by the > effects of and can affect operations on SQL-data or schemas in > concurrent SQL-transactions. The isolation level of a SQL- > transaction is SERIALIZABLE by default. The level can be explicitly > set by the <set transaction statement>. > > The execution of concurrent SQL-transactions at isolation level > SERIALIZABLE is guaranteed to be serializable. A serializable > execution is defined to be an execution of the operations of > concurrently executing SQL-transactions that produces the same effect > as some serial execution of those same SQL-transactions. A serial > execution is one in which each SQL-transaction executes to completion > before the next SQL-transaction begins.

Solution 2 - C#

A useful way to cut down writing boilerplate code is to wrap it in a builder class like so:

public static class TransactionScopeBuilder
{
    /// <summary>
    /// Creates a transactionscope with ReadCommitted Isolation, the same level as sql server
    /// </summary>
    /// <returns>A transaction scope</returns>
    public static TransactionScope CreateReadCommitted()
    {
        var options = new TransactionOptions
        {
            IsolationLevel = IsolationLevel.ReadCommitted,
            Timeout = TransactionManager.DefaultTimeout
        };

        return new TransactionScope(TransactionScopeOption.Required, options);
    } 
}

Then you can use it like this when creating a transaction scope:

using (var scope = TransactionScopeBuilder.CreateReadCommitted())
{
    //do work here
}

You can add other common transaction scope defaults to the builder class as you need them.

Solution 3 - C#

Well, I guess this is one of those "only the designer would definitely know" type of questions. But here are my two cents anyhow:

While Serializable is the most "limiting" isolation level (concerning locking, in a lock-based RDBMS, and thus concurrent access, deadlocks, etc.) it is also the most "safe" isolation level (concerning consistency of data).

So while requiring extra work in scenarios like yours (been there done that ;-), it make sense to opt for the safest variant by default. SQL Server (T/SQL) chooses to use READ COMMITTED, obviously applying other reasons :-)

Making it changeable by configuration, would then be a bad idea, because by fiddling with configuration you could render a perfectly working application to a broken one (because it might simply not be designed to work with anything else). Or to turn the argument around, by "hardcoding" the isolation level, you can make sure that your application works as expected. Arguably, the isolation level is not a good fit for a configuration option (while the transaction timeout indeed is).

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
QuestionBernhard KircherView Question on Stackoverflow
Solution 1 - C#Simon MourierView Answer on Stackoverflow
Solution 2 - C#AlmondView Answer on Stackoverflow
Solution 3 - C#Christian.KView Answer on Stackoverflow