DCSIMG
WCF Transactions – Barebones Demo – Part 3 - David Sackstein's Blog

WCF Transactions – Barebones Demo – Part 3

In this post we will develop the client side of a WCF service that is capable of participating in the client transaction.

Other posts in this series are:

WCF Transactions – Barebones Demo – Overview

WCF Transactions – Barebones Demo – Part 1 (Tools)

WCF Transactions – Barebones Demo – Part 2 (Service Code)

WCF Transactions – Barebones Demo – Part 4 (Analysis)

You can download the source code for the series from here.

In Parts 1 and 2 we implemented some tracing tools and the service code. Here we implement the client code and in the next and final post in the series we will run the demo and analyze the results.

The Client Code

Well, first I used Add Service Reference option to create a proxy for the service. (The service configuration file specifies that mex is supported). The proxy is automatically named TextContainerClient.

That done, lets think about what we want to accomplish.

I would like to the test affect of accessing a resource manager within a transaction that aborts. First, I would like to see how that works with a resource manager that is in process, then I would like to see what happens when the resource manager is accessed via a WCF service. Both, of course, are expected to rollback on any changes made within the client transaction.

In order to run both options without duplicating too much code, I used the Adapter Design Pattern defining the following Adapter interface in the Client project:

namespace TransactionalClient

{

    interface ITextContainer

    {

        string Get();

        void Set(string s);

    }

}

I implemented this code twice, once for the a local resource manager and a second time for the resource manager sitting behind the service.

Here is the local resource manager implementation:

namespace TransactionalClient

{

    class LocalTextContainer : ITextContainer

    {

        VolatileResourceManager<string> rm;

 

        public LocalTextContainer()

        {

            rm = VolatileResourceManager<string>.Instance;

        }

 

        #region ITextContainer Members

 

        public string Get()

        {

            return rm.Get() as string;

        }

 

        public void Set(string s)

        {

            rm.Set(s);

        }

 

        #endregion

    }

}

In this case the Adaptee is the VolatileResourceManager.

For the service implementation, the Adaptee is the TextContainerClient service proxy.

namespace TransactionalClient

{

    class ServiceTextContainer : ITextContainer

    {

        TextContainerClient tc;

 

        public ServiceTextContainer()

        {

            tc = new TextContainerClient();

        }

 

        #region ITextContainer Members

 

        public string Get()

        {

            return tc.Get() as string;

        }

 

        public void Set(string s)

        {

            tc.Set(s);

        }

 

        #endregion

    }

}

Now for the business logic that works with the ITextContainer interface:

namespace TransactionalClient

{

    class Program

    {

        void Run<T>() where T : ITextContainer, new()

        {

            T t = new T();

 

            t.Set("Set 1");

 

            try

            {

                using (TransactionScope ts = new TransactionScope())

                {

                    Console.WriteLine("Before call to service");

 

                    Transaction.Current.Log();

 

                    t.Set("Set 2");

 

                    Console.WriteLine("After call to service");

 

                    Transaction.Current.Log();

 

                    throw new Exception("Deliberate exception");

 

                    ts.Complete();

                }

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

            }

 

            string result = t.Get();

            Console.WriteLine("Result = {0}", result);

        }

    }

}

The generic Run method creates a new ITextContainer object and calls Set non-transactionally with the value “Set 1”. This call will take affect regardless of any unhandled exceptions thrown later on.

Then, within the scope of a transaction, Run attempts to call Set with “Set 2”. Immediately afterwards the client throws an exception which prevents the transaction from completing - instead it aborts.

Twice in the code we use the Log() extension method added to the Transaction class to display information about the current transaction.

The last line prints the value stored the ITextContainer object after leaving the transaction scope.

The expected behavior of this method is, of course, that the “Set 2” should be rolled back and “Set 1” should be printed on the screen.

Finally, to complete the picture, here is the Main:

namespace TransactionalClient

{

    class Program

    {

        . . .

 

        void Run()

        {

            Console.WriteLine("\n===================== Local =======================\n ");

            Run<LocalTextContainer>();

 

            Console.WriteLine("\n==================== Service ======================\n ");

            Run<ServiceTextContainer>();

        }

 

        static void Main(string[] args)

        {

            new Program().Run();

            Console.ReadLine();

        }

    }

The Main executes the transaction code in Run<> twice, once against a local resource manager and then against the service wrapper.

The Client Configuration File

The client side looks very much like the service side.

Note the use of the wsHttpBinding that supports transaction flow and the explicit setting of transactionFlow = true to allow transactions to flow through the binding to the service.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <client>

      <endpoint address="http://localhost/Services/TextContainer"

                binding="wsHttpBinding"

                bindingConfiguration="TransactionalBindingConfiguration"

                contract="ServiceReferences.ITextContainer" />

    </client>

    <bindings>

      <wsHttpBinding>

        <binding name="TransactionalBindingConfiguration" transactionFlow="true">

          <security>

            <transport realm="" />

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

  </system.serviceModel>

</configuration>

Ok. We are done writing code.

In the next post we will run the demo,  observe and analyze the results and consider some configuration changes and their meanings.

Published Monday, June 15, 2009 1:20 AM by David Sackstein

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above:
Powered by Community Server (Commercial Edition), by Telligent Systems