MeTitus
MeTitus

Reputation: 3428

TransactionScope with nested transactions

using System;
using System.Transactions;

namespace ConsoleApplication3
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var trans = new TransactionScope(TransactionScopeOption.RequiresNew);

            try
            {
                if (!ProcessMessage(true))
                {
                    MoveMessageToErrorQueue();
                }

                trans.Complete();
            }
            catch (Exception)
            {
                trans.Dispose();
            }
        }

        private static bool ProcessMessage(bool throwError)
        {
            //write to database

            var trans = new TransactionScope(TransactionScopeOption.Required);

            try
            {
                if (throwError)
                    throw new Exception();

                trans.Complete();

                return true;
            }
            catch (Exception)
            {
                trans.Dispose();
            }

            return false;
        }

        private static void MoveMessageToErrorQueue()
        {
            var trans = new TransactionScope(TransactionScopeOption.Required);

            trans.Complete();
        }
    }
}

How can I make the transaction scope inside the MoveMessageToErrorQueue method enlist in the main transaction, not in the one inside the ProcessMessage method?

When it is trying to create a new transaction inside the MoveMessageToErrorQueue I get an exception stating that the transaction has already been aborted.

UPDATE

I get the same result if I use a CommittableTransaction

using System;
using System.Transactions;

namespace ConsoleApplication3
{
    public class Program
    {
        public static void Main(string[] args)
        {
            using (var trans = new CommittableTransaction())
            {
                try
                {
                    if (!ProcessMessage(trans, true))
                    {
                        MoveMessageToErrorQueue(trans);
                    }

                    trans.Commit();
                }
                catch (Exception)
                {
                    trans.Rollback();
                }                
            }
        }

        private static bool ProcessMessage(Transaction trans, bool throwError)
        {
            //write to database

            var transScope = new TransactionScope(trans);

            try
            {
                if (throwError)
                    throw new Exception();

                transScope.Complete();

                return true;
            }
            catch (Exception)
            {
                transScope.Dispose();
            }

            return false;
        }

        private static void MoveMessageToErrorQueue(Transaction trans)
        {
            var transScope = new TransactionScope(trans);

            transScope.Complete();
        }
    }
}

Basically if the transaction within the ProcessMessage method fails I want to be able to still use the same root transaction within the last method.

Thanks for any help.

UPDATE 2

"in DTC transactions (and transactions that use that pattern), failure is fatal and immediate. One the doomed flag is set: it is set."

This says it all. I thought the behaviour was different.

Upvotes: 1

Views: 2476

Answers (2)

usr
usr

Reputation: 171178

With nested TransactionScopes there is still only one transaction. Nested scopes do not create a new transaction. Transactions cannot nest. System.Transactions does not support this. Neither does it support savepoints.

When you rollback a nested scope you doom the entire transaction.

I'm not sure how to get the behavior you want. With System.Transactions you have to avoid anything to doom the current transaction. Maybe you can use SQL Server's savepoints to undo the effects that a failing ProcessMessage had. Or, you can use a different DB connection for ProcessMessage and MoveMessageToErrorQueue.

Upvotes: 3

MeTitus
MeTitus

Reputation: 3428

"in DTC transactions (and transactions that use that pattern), failure is fatal and immediate. One the doomed flag is set: it is set."

Upvotes: 1

Related Questions