views:

145

answers:

1

I have had a good trawl through related questions and implemented suggestions found, but I still have a problem with .NET TransactionScope.

I am calling two WCF services from a method and even though the second service errors (deliberately in this case) the first service doesn't roll back. I have created a simple test application to demonstrate the problem.

Here is the calling method:

private void TryATransaction()
{
    ServiceReference1.IService1 service = new ServiceReference1.Service1Client();
    ServiceReference2.IService1 service2 = new ServiceReference2.Service1Client();

    using (TransactionScope scope = new TransactionScope())
    {
        service.TestMethod("one");
        service2.UpdateSomethingElse("two");

        scope.Complete();
    }
}

The first service looks like this (connections made anonymous):

public bool TestMethod(string anything)
{
        using (TransactionScope scope = new TransactionScope())
        {

            // connect to a db
            using (SqlConnection connection = new SqlConnection("Data Source=DATASOURCE;Initial Catalog=DATABASE;Integrated Security=SSPI;Transaction Binding=Explicit Unbind;"))
            {

                connection.Open();
                // save something
                SqlCommand command1 = new SqlCommand("EXECUTE [DATABASE].[dbo].[uspUpdateCustomer] 3, '[email protected]', 1, null", connection);
                command1.ExecuteNonQuery();
                connection.Close();

            }

            scope.Complete();
        }

    return true;
}

And the second service is identical to the first service, except that it updates a different field in the database.

1) If I run this without forcing an error, it updates all fields fine.

2) When I run this and force an error in the second service, the update from the first service gets committed to the database, whereas the second service gets rolled back (the error is AFTER the ExecuteNonQuery statement).

This example code essentially follows the examples found here: http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx

And I added Explicit Unbind, which was recommended in other related questions.

Your suggestions are very welcome - and I'm happy to add more information if it's required.

Extra Information

The distributed identifier seems to be 00000000-0000-0000-0000-000000000000 at all times - I don't know if this is a clue.

Here is the dubug output of distributed id and local id (in that order)

Current transaction is 00000000-0000-0000-0000-000000000000 - f6446876-496d-488c-a21c-1e4c4295d50c:8

Current transaction is 00000000-0000-0000-0000-000000000000 - 7edd5ba3-7f5a-42af-b9ca-37b3862c26a7:2

Current transaction is 00000000-0000-0000-0000-000000000000 - 6fa0e3f7-b655-40ad-8bdd-f0670de79a49:2

The transaction is being started via the code behind an aspx page in my example app.

+1  A: 

Edit: sorry had things backwards.

Most likely the issue is due to using Transactions together with WCF.

Check out this article on Transactional Services. In particular the part on Client/Service mode transactions.

Basically you need to:

  • Set the [TransactionFlow(TransactionFlowOption.Mandatory)] attribute on your OperationContract interface methods
  • Set the [OperationBehavior(TransactionScopeRequired = true)] attribute on their implementation
  • Add the TransactionFlowBindingElement to the BindingContext of your service
  • Remove the TransactionScope from your service's implementation, since you're using the client's TransactionScope.

Each service method that completes successfully votes for the transaction to succeed (when you are using the default TransactionAutoComplete=true OperationBehavior).

If a service method fails due to an exception, it votes for the transaction to fail.

Yannick M.
My problem is the reverse - I WANT the inner transaction to use the ambient transaction that I create in the method that calls the two services - so that if either of the services fail, the whole lot gets rolled back.
Sohnee
Yep - those attributes seem to be the key to this - thanks for coming back to adjust!
Sohnee
Glad I could help :-)
Yannick M.