Transaction Flow in Windows Communication Foundation

I'm pretty sure that everybody who reads my blog, has good information about transactions and their definitions but in one sentence transactions are a group of operations that act as an atomic unit.

WCF supports transaction flows out of the box.  But what does transaction flow mean?  Transaction flow means that if a client creates a proxy to one or more services and call their operations in a sequence and one of these operations fails then client completely fails.

There is a requirement to enable transaction flows for WCF: two-way communication between clients and services.

Apparently you know how to create a transaction using TransactionScope block and use its Complete() method to commit the transactions.

To enable transaction flow for your WCF applications you must enable it for service and client binding configurations explicitly (as you will see in a moment).  After this, you can use transaction flow in your applications without any problem.

I write a sample application (service and client) to show all details to enable transaction flow in WCF applications step by step.  If you need more information about writing WCF services and clients and hosting concepts, read my article about developing WCF applications on ASP Alliance.

Service

First of all I create a service project and a service contract for it (unlike VS 2005, in Orcas default project template for WCF services generates separate files for contract and service implementation).

using System;

using System.Runtime.Serialization;

using System.ServiceModel;

 

namespace MyService

{

    [ServiceContract]

    public interface IMyService

    {

        [OperationContract]

        int Method1(int x, int y);

 

        [OperationContract]

        int Method2(int x, int y);

    }

}

This service requires a service implementation as well.

using System;

 

namespace MyService

{

    public class TempService : IMyService

    {

        #region IMyService Members

 

        public int Method1(int x, int y)

        {

            return ((x + y) * 2);

        }

 

        public int Method2(int x, int y)

        {

            return ((x + y) * 4);

        }

 

        #endregion

    }

}

After compiling this service into a DLL file I use it to host my service on IIS.  This needs a .svc file and a Web.Config file.  In Web.Config I use wsHttpBinding as my binding but set its transactionFlow attribute to true to enable transaction flow for my service.

<?xml version="1.0"?>

<configuration>

  <system.serviceModel>

    <services>

      <service name="MyService.TempService"

              behaviorConfiguration="metadataSupport">

        <endpoint contract="MyService.IMyService"

                  binding="wsHttpBinding"

                  bindingConfiguration="MyWSBinding"/>

        <endpoint address="mex"

          binding="mexHttpBinding"

          contract="IMetadataExchange"/>

      </service>

    </services>

 

    <bindings>

      <wsHttpBinding>

        <binding name="MyWSBinding" transactionFlow="true"/>

      </wsHttpBinding>

    </bindings>

 

    <behaviors>

      <serviceBehaviors>

        <behavior name="metadataSupport">

          <serviceMetadata />

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Client

After creating my service and hosting it on IIS, I can generate my proxy class and client configuration file using SvcUtil.exe from command line.  Generated configuration file doesn't have transaction flow enabled for client by default so I edit it and set transactionFlow attribute of <binding /> element to true.

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

<configuration>

  <system.serviceModel>

    <bindings>

      <wsHttpBinding>

        <binding name="WSHttpBinding_IMyService" closeTimeout="00:01:00"

            openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"

            bypassProxyOnLocal="false" transactionFlow="true" hostNameComparisonMode="StrongWildcard"

            maxBufferPoolSize="524288" maxReceivedMessageSize="65536"

            messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"

            allowCookies="false">

          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"

              maxBytesPerRead="4096" maxNameTableCharCount="16384" />

          <reliableSession ordered="true" inactivityTimeout="00:10:00"

              enabled="false" />

          <security mode="Message">

            <transport clientCredentialType="Windows" proxyCredentialType="None"

                realm="" />

            <message clientCredentialType="Windows" negotiateServiceCredential="true"

                algorithmSuite="Default" establishSecurityContext="true" />

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

    <client>

      <endpoint address="http://keyvan-pc/MyService/service.svc" binding="wsHttpBinding"

          bindingConfiguration="WSHttpBinding_IMyService" contract="IMyService"

          name="WSHttpBinding_IMyService">

        <identity>

          <servicePrincipalName value="host/Keyvan-PC" />

        </identity>

      </endpoint>

    </client>

  </system.serviceModel>

</configuration>

And last step is to write my client code (ignore the reference to System.Linq.  It's because I wrote this application with .NET 3.5 and that reference is generated automatically).

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Transactions;

 

namespace MyClient

{

    class Program

    {

        static void Main(string[] args)

        {

            Console.Title = "Transaction Flow in WCF";

 

            Console.WriteLine("Enter x:");

            int x = Convert.ToInt32(Console.ReadLine());

            Console.WriteLine("Enter y:");

            int y = Convert.ToInt32(Console.ReadLine());

 

            using (MyServiceClient proxy = new MyServiceClient())

            {

                using (TransactionScope scope =

                    new TransactionScope(TransactionScopeOption.RequiresNew))

                {

                    Console.WriteLine(string.Format("({0} + {1}) * 2 = {2}",

                        x, y, proxy.Method1(x, y).ToString()));

                    Console.WriteLine(string.Format("({0} + {1}) * 4 = {2}",

                        x, y, proxy.Method2(x, y)));

 

                    scope.Complete();

                }

            }

            Console.ReadLine();

        }

    }

}

As you see, I created a proxy and put a TransactionScope block within it.  At the end of block, I used Complete() method to commit transactions.

Output

Now playing: Eminem - Mockingbird

[advertisement] Axosoft OnTime 2008 is four developer tools in one: bug tracking, project wiki, feature management, and help desk. It manages your development process so developers can focus on coding. Installed or Hosted – Free Single-user license -- Free 30-day team trial.

4 Comments : 05.11.07

Feedbacks

 avatar
#1
DotNetKicks.com
05.11.2007 @ 9:00 AM
You've been kicked (a good thing) - Trackback from DotNetKicks.com
 avatar
#2
JZO
09.22.2008 @ 6:14 AM

Hi,

Nice post, I have searched a good description about Transaction Flow. This is a very clear explanation for me, thanks!

Zoltan

 avatar
#3
WILSON
03.15.2009 @ 10:47 PM

Hi

nice post. i have a doubt

my code looks like

using (transactionscope myScope = new transactionscope())

{

//updations to a sql server which is residing on local machine

//calling a wcf service hosted in IIS on another machine over VSAT or internet, but defenitely on different domain

myScope.Complete()

}

to achieve the above stuff, do i need to do any kind of configurations on the local machine as well as on remote machine?

i read of installing certificates etc for achieving transactions if we use wsHttpBinding. do i need to do all those stuff?

 avatar
#4
dinesh
04.28.2009 @ 2:02 AM

hi ,

your post is really helpful for beginners like me. i worked on your sample and it works fine. thanks a lot.

Leave a Comment