Locked Misunderstanding about nested TransactionScope

  • Thursday, May 03, 2007 7:55 PM
     
     

    I'm exploring the TransactionScope  and don't quite understand what's going on.

    The following code run's fine for the two first TransactionScope,

    the third one throws an exception in the DoWithoutScope function saying that the operation is not valid for the transaction state ??

    Anyone knows why?

    Thanks

    Mateo

     

     

    Code Snippet

    namespace ConsoleClient

    {

    class Program

    {

      static void Main(string[] args)

      {

        using (Tracer t = new Tracer("Test"))

        {

          Foo foo = new Foo();

          foo.Name = "000";

          using (TransactionScope scope = new TransactionScope())

          {

            foo.DoWithoutScope();

          }

        

          foo.Name = "001";

          using (TransactionScope scope = new TransactionScope())

          {

            foo.DoRequired(true);

            foo.DoRequiresNew(true);

            foo.DoSuppress(true);

            foo.DoWithoutScope();

            scope.Complete();

          }

     

          foo.Name = "002";

          using (TransactionScope scope = new TransactionScope())

          {

            foo.DoRequired(false);

            foo.DoRequiresNew(false);

            foo.DoSuppress(false);

            //THIS WILL THROW AN EXCEPTION

            foo.DoWithoutScope();

          }

        }

      }

    }

    public class Foo

    {

      private Database _db;

      private String _name;

      public Foo()

      {

        _db = DatabaseFactory.CreateDatabase("TestDB");

      }

      public string Name

      {

        get { return _name; }

        set { _name = value; }

      }

     

    public void DoRequired(bool Complete)

    {

      using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))

      {

        try

        {

          DbCommand cmd = _db.GetStoredProcCommand("AddClient");

          _db.AddInParameter(cmd, "Nom", System.Data.DbType.String, _name + " (Required)");

          _db.AddInParameter(cmd, "Prenom", System.Data.DbType.String, "Mr.");

          _db.ExecuteDataSet(cmd);

          if(Complete)

            scope.Complete();

        }

        catch (Exception exc)

        {

          Console.WriteLine(string.Format("DoRequired: {0}", exc.ToString()));

        }

      }

    }

    public void DoRequiresNew(bool Complete)

    {

      using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))

      {

        try

        {

          DbCommand cmd = _db.GetStoredProcCommand("AddClient");

          _db.AddInParameter(cmd, "Nom", System.Data.DbType.String, _name + " (RequiresNew)");

          _db.AddInParameter(cmd, "Prenom", System.Data.DbType.String, "Mr.");

          _db.ExecuteDataSet(cmd);

          if(Complete)

            scope.Complete();

        }

        catch (Exception exc)

        {

          Console.WriteLine(string.Format("DoRequiresNew: {0}", exc.ToString()));

        }

      }

    }

    public void DoSuppress(bool Complete)

    {

      using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress))

      {

        try

        {

          DbCommand cmd = _db.GetStoredProcCommand("AddClient");

          _db.AddInParameter(cmd, "Nom", System.Data.DbType.String, _name + " (Suppress)");

          _db.AddInParameter(cmd, "Prenom", System.Data.DbType.String, "Mr.");

          _db.ExecuteDataSet(cmd);

          if (Complete)

            scope.Complete();

        }

        catch (Exception exc)

        {

          Console.WriteLine(string.Format("DoSuppress: {0}", exc.ToString()));

        }

      }

    }

    public void DoWithoutScope()

    {

      try

      {

        DbCommand cmd = _db.GetStoredProcCommand("AddClient");

        _db.AddInParameter(cmd, "Nom", System.Data.DbType.String, _name + " (WithoutScope)");

        _db.AddInParameter(cmd, "Prenom", System.Data.DbType.String, "Mr.");

        _db.ExecuteDataSet(cmd);

      }

      catch (Exception exc)

      {

        Console.WriteLine(string.Format("DoWithoutScope: {0}", exc.ToString()));

      }

    }

    }

    }

     

All Replies

  • Thursday, May 03, 2007 9:51 PM
    Moderator
     
     Answered

    Sure thing:

     

    Let's take the first example, I've added comments that describes what's happening a bit:

    Code Snippet

          foo.Name = "001";

          using (TransactionScope scope = new TransactionScope()) // A new transaction (tx1) is created

          {

            foo.DoRequired(true);    // tx1 is used (no new transaction is created) and this inner scope completes. tx1 is still active and valid

            foo.DoRequiresNew(true); // A new transaction (tx2) is created because of RequiresNew. The scope is completed and tx2 commits and goes away

            foo.DoSuppress(true);    // tx1 is suppressed so Completing/Not-Completing this scope does not affect tx1

            foo.DoWithoutScope();    // tx1 is still active and valid so DoWithoutScope uses tx1 again -- SQL automatically picks up the ambient tx1

            scope.Complete();        // All is well

          }

     

    The second example:

     

         

    Code Snippet

     foo.Name = "002";

          using (TransactionScope scope = new TransactionScope()) // A new transaction (tx1) is created

          {

            foo.DoRequired(false);    // tx1 is used. However because you did not complete the scope, tx1 is marked for Rollback. No new work is allowed

            foo.DoRequiresNew(false); // A new transaction (tx2) is created because of RequiresNew. The scope is not-completed so tx2 rolls back

            foo.DoSuppress(false);    // tx1 is suppressed so Completing/Not-Completing this scope does not affect tx1

            //THIS WILL THROW AN EXCEPTION

            foo.DoWithoutScope();     // tx1 is not allowed to be used anymore. However, DoWithoutScope uses SQL and SQL will automatically try to use tx1

          }

     

  • Friday, May 04, 2007 2:13 AM
     
     
    Sure thing now!

    "No new work is allowed"

    i was missing this part. quite simply said but way more clear than the sdk.
    Thanks a lot for your help


  • Wednesday, May 09, 2007 2:59 PM
     
     

    It is very clear explaination. Thank you.

    But I still have one queston in first example.

    foo.DoWithoutScope() uses tx1,

    Is that means if any error occurred in foo.DoWithoutScope(), will foo.DoRequired() rollback?

     

    Thank you.

     

  • Tuesday, May 22, 2012 9:18 PM
     
     

    I am working on a similar issue..I need a tx2 withing tx1 and tx2 should rollback on certain conditions.But none of the cases work for me .I tried tx1 -New and tx2-New but resulted in an error.

    Do you have a solution for this