he previous two articles in this series provided an overview of the Windows Communication Foundation (WCF, formerly code-named Indigo) programming model, service addressing, binding and contracts, and a discussion of how to secure Windows Communication Foundation services using the built-in attribution method.
If you haven’t already done so, it would be a good idea to go through those articles to get up and running quickly with WCF. In particular, if you are having any trouble getting a development environment installed and working, you’ll find that those articles explain the process.
The pillars of WCF are intended to help you build secure, reliable, and transactable services for .NET. In this article, you will build on the code from the previous two articles to create your first transactable service.
Transactions in WCF
Transactions ensure that a group of related operations occur as a single atomic unit. In other words, every operation in the unit must either all succeed or all fail. WCF provides a centralized transaction system that you can use to handle transaction operations for you. In the past, transaction logic was commonly available as a standard way to handle database transactions (remember BeginTrans and CommitTrans in VB?), but there was no standard way to perform non-database transactions. WCF aims to solve this with a single unified transaction system that you can use for database, communications, or other transactable actions.
The WCF programming model makes transactions very easy to use. You group operations into a transaction scope. This scope defines the atom of a transaction. The following pseudo code demonstrates this:
Using(TransactionScope theScope = new TransactionScope()) { Service1.submitRequest(myRequest); Service2.submitRequest(myOtherRequest); Service3.submitRequest(myFinalRequest); theScope.Complete(); }
Building Your First Transactable Service
To get started, you first need a service that is transactable. This means that you are willing to let your service participate in a client-initiated transaction on your contracted operations. You will specify (using attributes and configuration files) how this transaction will behave. After building the service, you’ll step through the process of building a client that uses a transaction scope when calling the service. If you have set up Visual Studio.NET as outlined in the WCF security article, you should be able to get up and running very quickly.
To get started, create a new Web Site in Visual Studio.NET. Select the IndigoService project type, and call it TService. Your screen should look like Figure 1.
![]() | ? |
Figure 1. Creating a New Indigo Service: After selecting the Indigo Service project type and giving it the name TService, your screen will look like this. |
Visual Studio will create a default service for you with an interface called IMyService, and a service class called MyService. You’ll find the service class code in the Service.cs file in the App_Code subfolder. Replace the code in this file with the code shown in Listing 1. You’ll also need to add a reference to the System.Transactions namespace for the code to work correctly when you compile it.
Note that the code for the transactable Web service is identical to the original “temperatures” Web service from the primer tutorial except for the new attributes that describe it as being transactable.
At the interface level, when the methods are defined, the OperationContract is attributed with a [TransactionFlow] attribute. This notifies the runtime of how to respond in a transaction situation when operating on the OperationContract in question. The valid values are Allowed whereby the operation may or may not be used in a transaction; NotAllowed where it is never to be used in a transaction, and Required where it must only be used within a transaction scope.
At the service level, the [ServiceBehavior] attribute specifies the transaction properties, using the TransactionIsolationLevel property. Ideally transactions should exhibit the four key properties of being atomic, consistent, isolated and durable, described by the acronym ACID. However, keeping them fully isolated from one another can result in resource locks being held longer than necessary, which can cause lock contention, degrading performance, or even deadlock situations, where different transactions each need locks held by the other transaction to complete. Setting the isolation level lets you choose the degree of isolation most compatible with other applications. You set the isolation level by setting the IsolationLevel property value to the ServiceBehavior’s TransactionIsolationLevel property value. In Listing 1, the IsolationLevel is set to ReadCommited, meaning that volatile data cannot be read in the transaction, but can be modified. For a full exploration of the different isolation level property values you can use, refer to the documentation for the System..Transactions.IsolationLevel enumeration. Note that there’s also a System.Data.IsolationLevel enumeration, so if you refer to the System.Data namespace in your code then you should fully qualify the IsolationLevel to avoid confusion.
Finally, Listing 1 defines the transaction behavior for the Web method within the service using the [OperationBehavior] attribute, which has its TransactionScope property set to true. This TransactionScope setting indicates that the operation must be called within a scope (as will be demonstrated within the client). The TransactionAutoComplete property is also set to true to indicate that the transaction will be flagged as complete when the method finishes executing. If the TransactionAutoComplete property were set to false, then you would have to call the OperationContext.Current.SetTransactionComplete() method to set the correct completion of the transaction manually; otherwise the transaction would flag it as failed.
Running the Service
To run this service, you’ll need to do a little more housekeeping. First, you must change the Service.svc file to point to the new class and interface. In that file, find the line that reads:
Class="MyService"
Change it to:
Class="TransactableTemperatures"
Next, configure the runtime to allow transactions; otherwise running the service will cause an error. To do this, from a command prompt, issue the following command:
Xws_reg --wsat+
The command configures MSDTC to handle WS-Atomic Transactions (WSAT).
Finally, of course, you need to edit the web.config file to configure the serviceModel setting for your service. The full web.config file should look something like this:
![](http://assets.devx.com/articlefigs/14406.jpg)
? Figure 2. Running the Service: The figure shows the result of running the sample transactional service from a browser.
With the web.config changes in place, running the service will now give you a screen that looks something like Figure 2.
Building a Client for the Service
With the service in place, you can add a Windows Form client application to the project. Lay out the basic form with labels, options and buttons as shown in Figure 3, or just use the version from the download.
![]() | ? |
Figure 3. Designing the Client: The figure shows the sample Windows Forms client in the Visual Studio forms designer. |
A user enters a value into the text box, selects a conversion method using the radio buttons, and clicks the Convert button to call the service. But before writing code to make that happen, you need to create a proxy to the service. To do this, follow the instructions on the service page (see Figure 2), using the svcutil.exe tool to generate a proxy class file and a client configuration file. I’ve found it’s easier not to use the generated config file (called output.config); instead, simply create your own version that uses the portions required for the application at hand, and call it App.config.
To run svcutil.exe, run the following command from a command prompt.
Scvutil ?wsdl
The preceding command creates two files, which will likely be named tempuri.org.cs and output.config. The tempura.org.cs file contains the proxies that you need to call your service, so add it to your Windows Client project.
Then, add a new file called App.config to the project. Give it the following contents (these are filtered from output.config for simplicity):
Now you can set the btn_Click event handler to call the transactable service using a transaction scope, like this:
private void btnConvert_Click( object sender, EventArgs e) { using(TransactableTemperaturesProxy theProxy = new TransactableTemperaturesProxy("default")) { TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted; ??using ( TransactionScope tx = new TransactionScope( TransactionScopeOption.RequiresNew, transactionOptions)) ? { double d = Convert.ToDouble(txtIn.Text); ? double dResult = 0.0D; ?? if (rbCToF.Checked) ?? ? dResult = theProxy.ctof(d); ???? else ??? dResult = theProxy.ftoc(d); lblOut.Text = dResult.ToString(); ? ?? tx.Complete(); ?? } } }
The preceding code first sets up the proxy, using the “default” configuration name. You can see this is the name of the configuration within App.config.
Next the code sets up a new set of transactionOptions, configured for IsolationLevel.ReadCommitted (matching the service IsolationLevel setting, as discussed earlier).
Finally, the code sets up a new TransactionScope called tx within the using{} block, so that everything that occurs within this block is transacted within the tx scope. The last line of code in the block flags tx as Complete, and the transaction finishes successfully.
Transactions are vitally important in any connected system. Until now, developers have typically equated transactions with database methodology, but transactions can apply equally well to other types of operations. In connected systems, messages being passed around or orchestrated within a workflow can benefit greatly from transactability. Offering transactionable services from your code is critical when putting together a robust, enterprise-class infrastructure. Thus, transactability is one of the major pillars of the Windows Communication Foundation. In this article you’ve seen how to build a simple transactable service, and how to consume it from a Windows Forms client. While this example only scratches the surface of the possibilities, it should let you hit the ground running to build and offer transactable software services to your clients!