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
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
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:
Building Your First Transactable Service
Using(TransactionScope theScope = new
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
, 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.