Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested Transactionscope , Required -> Suppress -> Required

Which Transaction does the inner TransactionScope take as ambient transaction ?

using ( var t1 = new TransactionScope( TransactionScopeOption.Required ) )
{
    //MyStuff

    using ( var t2 = new TransactionScope( TransactionScopeOption.Suppress) )
    {
        //MyStuff

        using ( var t3 = new TransactionScope( TransactionScopeOption.Required) )
        {
            //Mystuff

            t3.Complete();
        }

        t2.Complete();
    }

    t1.Complete();
}
like image 386
KroaX Avatar asked Jan 16 '23 07:01

KroaX


2 Answers

t3. There is nothing else to choose from there, since the scope of t2 is to supress t1, not to create it's own ambient. Therefore at the innermost scope there is only t3 and nothing else.

If t2 would be RequireNew then the innermost scope ambient would be t2 since t3 would join t2.

like image 193
Remus Rusanu Avatar answered Jan 29 '23 14:01

Remus Rusanu


Keep in mind: a suppressed TransactionScope do not need to "complete"

There is no transaction.

For example you could do this here:

    static void TestMethod()
    {
        using (Model1 ctx = new Model1())
        {
            Console.WriteLine("Count = {0}", ctx.Test.Count());
            Console.WriteLine("Has Value 'Test1' = {0}", ctx.Test.Count(x => x.Value == "Test1"));
            Console.WriteLine("Has Value 'Test2' = {0}", ctx.Test.Count(x => x.Value == "Test2"));
        }

        using (Transactions.TransactionScope scope = new Transactions.TransactionScope())
        {
            using (Model1 ctx = new Model1())
            {
                ctx.Test.Add(new Test { Value = "Test1" });

                Console.WriteLine("Add 'Test1'");
                Console.WriteLine("SaveChanges = {0}", ctx.SaveChanges());
            }

            using (Transactions.TransactionScope scope2 = new Transactions.TransactionScope(Transactions.TransactionScopeOption.Suppress))
            {
                using (Model1 ctx = new Model1())
                {
                    ctx.Test.Add(new Test { Value = "Test2" });

                    Console.WriteLine("Add 'Test2'");
                    Console.WriteLine("SaveChanges = {0}", ctx.SaveChanges());
                }
            }
        }

        using (Model1 ctx = new Model1())
        {
            Console.WriteLine("Count = {0}", ctx.Test.Count());
            Console.WriteLine("Has Value 'Test1' = {0}", ctx.Test.Count(x => x.Value == "Test1"));
            Console.WriteLine("Has Value 'Test2' = {0}", ctx.Test.Count(x => x.Value == "Test2"));
        }
    }

Output:

  • Count = 1
  • Has Value 'Test1' = 0
  • Has Value 'Test2' = 0
  • Add 'Test1'
  • SaveChanges = 1
  • Add 'Test2'
  • SaveChanges = 1
  • Count = 1
  • Has Value 'Test1' = 0
  • Has Value 'Test2' = 1

That means every query in a suppressed scope cannot rollback by just disposing the scope.

But if you do this:

    static void TestMethod()
    {
        using (Model1 ctx = new Model1())
        {
            Console.WriteLine("Count = {0}", ctx.Test.Count());
            Console.WriteLine("Has Value 'Test1' = {0}", ctx.Test.Count(x => x.Value == "Test1"));
            Console.WriteLine("Has Value 'Test2' = {0}", ctx.Test.Count(x => x.Value == "Test2"));
            Console.WriteLine("Has Value 'Test3' = {0}", ctx.Test.Count(x => x.Value == "Test3"));
        }

        using (Transactions.TransactionScope scope = new Transactions.TransactionScope())
        {
            using (Model1 ctx = new Model1())
            {
                ctx.Test.Add(new Test { Value = "Test1" });

                Console.WriteLine("Add 'Test1'");
                Console.WriteLine("SaveChanges = {0}", ctx.SaveChanges());
            }

            using (Transactions.TransactionScope scope2 = new Transactions.TransactionScope(Transactions.TransactionScopeOption.Suppress))
            {
                using (Model1 ctx = new Model1())
                {
                    ctx.Test.Add(new Test { Value = "Test2" });

                    Console.WriteLine("Add 'Test2'");
                    Console.WriteLine("SaveChanges = {0}", ctx.SaveChanges());
                }

                using (Transactions.TransactionScope scope3 = new Transactions.TransactionScope())
                {
                    using (Model1 ctx = new Model1())
                    {
                        ctx.Test.Add(new Test { Value = "Test3" });

                        Console.WriteLine("Add 'Test3'");
                        Console.WriteLine("SaveChanges = {0}", ctx.SaveChanges());
                    }

                    scope3.Complete();
                }
            }
        }

        using (Model1 ctx = new Model1())
        {
            Console.WriteLine("Count = {0}", ctx.Test.Count());
            Console.WriteLine("Has Value 'Test1' = {0}", ctx.Test.Count(x => x.Value == "Test1"));
            Console.WriteLine("Has Value 'Test2' = {0}", ctx.Test.Count(x => x.Value == "Test2"));
            Console.WriteLine("Has Value 'Test3' = {0}", ctx.Test.Count(x => x.Value == "Test3"));
        }
    }

Output:

  • Count = 0
  • Has Value 'Test1' = 0
  • Has Value 'Test2' = 0
  • Has Value 'Test3' = 0
  • Add 'Test1'
  • SaveChanges = 1
  • Add 'Test2'
  • SaveChanges = 1
  • Add 'Test3'
  • SaveChanges = 1
  • Count = 2
  • Has Value 'Test1' = 0
  • Has Value 'Test2' = 1
  • Has Value 'Test3' = 1

You can see: there is no relation between "scope" and "scope3", because "scope3" commit its changes.

Also "scope2" commit its changes, because "scope2" is a suppressed scope.

like image 36
Marcus.D Avatar answered Jan 29 '23 12:01

Marcus.D