WCF - Transactions

An existing need in many applications is to ensure data consistency during handling. When performing a task, we need to ensure that if a problem occurs, the data back to its initial state. In computing this is guaranteed by the use of transactions. Transactions have been around for some time, and the purpose of this article is to show the alternatives we have to incorporate them into services and clients that make use of WCF as a means of communication. A classic example that demonstrates the need for transactions is when we need to create a routine transfer of money between two accounts. The transfer basically consists of two operations: withdraw money from an account and make a deposit into another. If a problem occurs between the withdrawal and the deposit and it is not wrapped in a transaction, the source account will without the money and the destination account does not receive the amount, making the data involved (balance of origin) is in an inconsistent state.Setting a transaction consists of a set of operations (often complex) that if any of them fails, the whole process should fail, ie, an atomic operation (or all, or none). The atomicity is one of the four characteristics that every transaction must have, namely: The tomicidade, C onsistĂȘncia, I solamento and D urabilidade (ACID).Consistency ensures that if any of the operations that are involved in a transaction fails, it will ensure that data back to its initial state, ie, the same information before the transaction started; already isolation ensure that no other entity will access the data that is being modified during the current transaction, preventing others from accessing such a value that may not be the final figure. Finally, and not least, the durability ensures that once she was "uncommitted", the transaction is effectively persisted, resisting the possible faults in the application / database. Traditionally, transactions have been associated with the database, but they can be applied in a series of transactions involving changes in data. Currently the need goes beyond transactions to ensure the consistency of records from a database or other repository. With services increasingly in evidence and being developed by many companies to provide some functionality, the need to involve the call to these services in a single transacted operation made ​​the transactions were implemented to ensure the spread of it a generic form for different services, processes, organizations and platforms. As discussed above, a transaction is a set of operations that mostly involve transactional resources, such as database and even the Message Queue . These transactional resources are (should be) able to perform the commit or rollback the possible changes that were made ​​to the data. Involve one of these resources is called a transaction enlistment ; there are also some features that can detect being accessed by a transaction, and automatically he will be part of it. This technique is known as auto-enlistment . Within a transaction, there is always the application that initiates, known as "coordinator"; since the others are referred to as "participants". The communication between the coordinator and the other participants should be performed to ensure atomicity as consistency, as already mentioned above. In this context, it is guaranteed by a protocol popularly known as two-phase commit protocol (2PC). As the name implies, it consists basically in two phases to ensure the commit or rollback of the information, abstracting all the great complexity that lies behind this process.Below the description of each phase:









  • Step 1: Preparation. At this stage, everyone involved (participants) in the transaction send a notification to the coordinator of the same, stating that he is prepared to perform the commit or rollback (vote).
  • Step 2: Commit or Rollback . By collecting all votes (of commit or rollback ), the coordinator will decide what should be done. If any participant voted as rollback , then the coordinator will notify all participants to perform the rollback of the transaction; If everyone vote as commits , then the coordinator sends a notification to each participant to commit the changes.
NOTE: The state of data between stage 1 and stage 2 is known as in-doubt state . As mentioned above, the isolation of the four characteristics of the transaction, ensure that this information is not accessed by any other transaction and that there are no inconsistencies. Transactional resources are divided into two categories: durable goods ( durable ) and volatile ( volatile ). A durable transactional resource is able to save the information during the first phase of the protocol two-phase commit , and even if a problem occurs in the machine and it needs to be reset, it can continue in the transaction (eg Microsoft SQL Server). Already volatile resources can be enlisted to receive notifications of the protocol two-phase commit , but they do not resist the possible problems that may occur, ie, they can not survive a more severe failure such as a system reboot. The creation of volatile resources is outside the scope of this article but, as a reference, there are classes and interfaces available in theSystem.Transactions Assembly that enable the creation of them. still talking about the technologies that surround the transaction, we have the protocols (here, the word protocol refers to a form of communication) that allow this process to happen. Currently we have three different protocols, and each with a different purpose. Below is each of them with their respective description:


  • Lightweight : This is the highest performing protocol in relation to the other two, but has a limitation that it can not propagate the transaction context outside of the application domain ( AppDomain ).
  • OleTx : Unlike Lightweight , protocol OleTx can be propagated through the application domain (AppDomain ), processes and machines. Because it is a native Windows protocol, it can not exceed firewallsor even interoperate with other platforms. Generally it is used in an environment of intranet and completely homogeneous, where these "limitations" are not problems.
  • WS-Atomic Transaction (WS-AT): This protocol is similar to OleTx and can propagate between the application domain ( AppDomain ), processes and machinery, managing the two-phase commit . The advantage of this protocol over the previous is that it is based on an open standard that can be implemented on any platform. Furthermore, it can be used under HTTP and internet , through possiblefirewalls within the existing infrastructure.
Finally, we have transaction managers ( Transaction Managers ). Seen briefly as works two-phase commit and, fortunately, all we need do is say if it worked or not and he will take care of the rest. But who manages all this? All this responsibility is borne by the Transaction Managers . They are who enlist transactional resources (databases,message queues ) within the transactional environment and make use of one of the above protocols to determine the commit or rollback . Currently we have three transaction managers, namely:
  • Lightweight transaction manager (LTM): This manager is only able to deal with a local transaction, or within a AppDomain . He uses the protocol Lightweight to manage the two-phase commit . He can manage a single durable resource and various volatile.
  • Kernel Transaction Manager (KTM): KTM, in turn, allows to manage the transactional level of resourceskernel (KRM), as is the case with files (TXF) and also the system registry (TXR) by making use of protocolLightweight . Just as before, the KTM can manage a single resource KRM and various volatile resources.Only supported on Windows Vista.
  • Distributed Transaction Coordinator (DTC): DTC is the most comprehensive of all. He is able to manage a transaction without a limit of scope, ie, the transaction can be propagated across multiple AppDomains , different processes and machines. DTC natively supports the protocol OleTx and, more recently, it has been adapted to also support WS-AT protocol, the pertindo he interoperability with non-Microsoft services and customers.
Note: By default, the protocol support WS-Atomic Transaction (WS-AT) in DTC is disabled. So you can manipulate this setting, it is first necessary that you can view it and for that, you must run the following command line (viaprompt Visual Studio NET): regasm.exe / wsatui.dll codebase . Once run this command, you can go to Control Panel , Administrative Tools , Component Services , Properties of the Distributed Transaction Coordinator and see the WS-AT tab at your disposal, as shown below:
Figure 1 - Enabling WS-AT in DTC.

Transactions in NET 2.0 

From version 2.0 of the NET Framework, Microsoft introduced a new programming model for supporting transactions in managed code. This is the System.Transactions namespace ( Assembly System.Transactions.dll ).This new namespace provides several types (classes, interfaces , etc.) so you can manipulate transactions, enlist durable or volatile resources and working with local or distributed transactions. This new programming model allows a developer to determine a (block) section of code it will be wrapped by the transaction. Whatever you make within this block will be protected by a transaction, being under the responsibility of the developer say if she successfully completed to later be "uncommitted". This namespace already supports the protocol Lightweight Transaction Manager (LTM), promoting it transparently to OleTx when for some limitation, the LTM can not be used. A major classes used is the class TransactionScope . This class is responsible for determining a transactional block within the application. Generally, this class is created and surrounded by a block using and, within it, we make the calls to the resources (databases, message queues , etc.) that will be part of the transaction. Once all tasks complete successfully, you must call the method Complete class TransactionScope ; otherwise, you just should not call this method, and automatically the rollback happen. The code snippet below illustrates an example of how to use it:





TransactionOptions TransactionOptions opts = new ();
opts.IsolationLevel = IsolationLevel.ReadCommitted;
opts.Timeout = TimeSpan.FromMinutes (1);

using (TransactionScope ts = 
    new TransactionScope (TransactionScopeOption.RequiresNew, opts))
{
    RecuperarMensagem Message msg = ();
    if (msg! = null)
        InserirMensagemNoBancoDeDados (msg);

    ts.Complete ();
}

It is important to note that if for any problem an exception occurs during the execution of the methodRecuperarMensagem or InserirMensagemNoBancoDeDados , Method Complete is not triggered, and therefore will be the rollback automatically (in this case, the check is performed within the method Dispose , which will always be invoked). Already both methods do not trigger any exception, the method Complete will be called, and all modifications will be effective. Funds provided for handling transactions do not stop there. There are several features to be available, as the case of the creation and recruitment of resources volatile but, unfortunately, are beyond the scope of this article. If you want to know about more detail about how transactions work within the NET, then you may use the documentation for this namespace directly from MSDN. Transactions Using the WCF As services is gaining more space and one of the main requirements is that they have just need to wrap it in a transaction, causing it to be a coordinator or participant of a transactional process. Fortunately, Microsoft bothered with this and provided through classes and attributes a number of features within the WCF to handle transactions. Importantly, support and configuration transactions are characteristics of binding , ie, is the bindingthat will determine the support or not the transaction under which the defining protocols (WS-AT or OleTx ) the transaction will be exposed. Optionally, the binding can also set a timeout and the spread of the same customer for the service (will be discussed later, even this article). Like almost all the features in WCF, the transaction setting can be imperative or declarative way. How it all begins with the contract, then it is that we initially create.He will have two simple methods: Add and Retrieve , where the first one should be wrapped in a transaction and the second not. Note that the code below illustrates the structure of the contract, but without any configuration transaction; WCF configuration decouples the definition of the contract transaction (except one, which will be discussed later), the developer required to configure this implementation, through behaviors .











using System;
using System.ServiceModel;

[ServiceContract]
public interface IUsuarios
{
    [OperationContract]
    bool Add (string name);

    [OperationContract]
    string [] Retrieve ();
}

Once this contract implemented in a class that will represent the service, the first step is to determine whether or not a transaction should run in a transacted environment that, by default, is disabled and in this case, even if a transaction is propagated to the client service, it is ignored. To enable the use of the transaction in a transaction, simply resort to property TransactionScopeRequired attribute OperationBehaviorAttribute , setting it to True , as shown by the example code below:

[ServiceBehavior (
    TransactionTimeout = "00:02:00"
    , TransactionIsolationLevel = IsolationLevel.ReadCommitted)]
public class ServicoDeUsuarios: IUsuarios
{
    [OperationBehavior (TransactionScopeRequired = true)]
    public bool Add (string name)
    {
        // Implementation

        return true;
    }

    [OperationBehavior (TransactionScopeRequired = false)]
    public string [] Retrieve ()
    {
        // Implementation

        return null;
    }
}

Optionally, you can set through attribute ServiceBehaviorAttribute a timeout and isolation level. The timeoutspecified from the property TransactionTimeout ( Timespan ), specify the period between the creation and completion (with commit or rollback ) transaction. If it is not completed by the specified time in this property, the transaction is automatically aborted. The level of isolation, defined through the property TransactionIsolationLevel , will receive one of the options specified in the enumerator IsolationLevel , which is contained in theSystem.Transactions namespace , and when not specified, the option Serializable is used. Because the data are modified by the transaction are considered in-doubt , is through the isolation level that will determine whether these changes may or may not be accessed before the transaction is completed. Importantly, both properties directly affect all operations which have the property TransactionScopeRequired set to True . In the above, only the method Add will be engaged in a transactional context. Explicitly define the propertyTransactionScopeRequired to False in the method Recover , but remember that hiding this attribute will get the same result, ie, the operation will not be traded. To know whether or not the transaction has been created, we can resort to class Transaction , a major class of System.Transactions namespace . This class provides a static property called Current , which returns an instance of the class Transaction , representing the current transaction and when the return is empty, then no transaction was created. If the method supports transaction and it is automatically created any transactional resource you go into this operation, will be listed automatically ( auto-enlistment ), and with it, any manipulation will be managed by the transaction. That means no need to write code to create the transaction, because the WCF already provides that; regardless of the transaction created by WCF, you can seamlessly within the method, create a transactional block by class TransactionScope that one of its constructors accept one of the options specified in the enumerator TransactionScopeOption , which allows to "interact" with the existing environment transacted. Possible values ​​are:


  • Required: A transaction is required. If the transaction exists, the process will be part of it; Otherwise, a new transaction will be created.
  • RequiredNew: A new transaction is required. Regardless or not if there is a transaction will always be created new.
  • Suppress: not a transaction is required. Regardless or not if there is a transaction, the task will not be involved in a transacted environment.
The example below shows how we can proceed to suppress a transaction inside a method that has requested a transaction to WCF. Note that only part of the method will be suppressed, not influencing the vote of the transaction ( commit or rollback ).
[ServiceBehavior (
    TransactionTimeout = "00:02:00"
    , TransactionIsolationLevel = IsolationLevel.ReadCommitted)]
public class ServicoDeUsuarios: IUsuarios
{
    [OperationBehavior (TransactionScopeRequired = true)]
    public bool Add (string name)
    {
        // Operations

        using (TransactionScope scope = 
            new TransactionScope (TransactionScopeOption.Suppress))
        {
            // Block not transacted
        }

        return true;
    }
}

Making the Vow 

Once we know how to set up an operation to support transactions, we now need to know how to apply the vote, or tell the runtime if the transaction was successful or if a fault has occurred. There are two ways to make the vote:. Declarative (via attributes) or imperative (via code) In declarative mode must use the propertyTransactionAutoComplete , also exposed by the attribute OperationBehaviorAttribute . This property, of type boolean (the default is True ), when set to True , will determine which, if not treated any exception occurs during the execution of the operation / method, the transaction should be marked as "completed" when the method returns; if for some reason an exception occurs, the transaction is aborted. When this property is set to False , the behavior is slightly different, ie, the transaction will be linked to the service instance (*) and will only be marked as "completed" if the client make a new call to a method that also has this property set to True or invoke the method when SetTransactionComplete . (*) This will cause the service to be defined as PerSession. Use the static method SetTransactionComplete defined in class OperationContext , is necessary when the propertyTransactionAutoComplete is set to False . The idea here is to allow the developer to determine when is the appropriate time to mark the transaction as "complete." This gives greater flexibility, because not always is an exception that will determine whether or not the transaction should be aborted. The example below illustrates how we can make use of this method:







[ServiceBehavior (
    TransactionTimeout = "00:02:00"
    , TransactionIsolationLevel = IsolationLevel.ReadCommitted)]
public class ServicoDeUsuarios: IUsuarios
{
    [OperationBehavior (TransactionScopeRequired = true, TransactionAutoComplete = false)]
    public bool Add (string name)
    {
        if (true)
        {
            AdicionarNoBancoDeDados (name);
            if (NotificarGerentes (name))
            {
                OperationContext.Current.SetTransactionComplete ();
                return true;
            }
        }

        return false;
    }
}

As we can see in the above code, the property TransactionAutoComplete is set to False , in which case it is for the developer to determine if the transaction should be "completed". Inside the method, when the inclusion in the database happen and if the method NotificarGerentes is performed successfully, the methodSetTransactionComplete will be invoked. If the notification does not happen for managers, the methodSetTransactionComplete will not be invoked and when the method Add return, the transaction is aborted. Note: If the call method SetTransactionComplete and there is not a transactional environment, an exception of typeInvalidOperationException will be thrown . Propagating Transactions Because all settings are server-side (viabehaviors ), they affect only the implementation and execution of the service, and customers will not undergo any change. But what if by chance, we want the client to be also involved in the transaction? For that, WCF provides an interesting feature called the propagation of the transaction. Propagation of the transaction will allow the service to forward an existing client transaction, causing the execution of the operation also part of it, instead of WCF to create it. The configuration to support the spread is defined in two places: in the contract and binding .The configuration of the contract will ensure that the service is exposed under a binding that supports this feature, just that. The configuration in the contract is defined by the attribute TransactionFlowAttribute that, in its constructor, should receive one of the three options defined by the enumerator TransactionFlowOption :








  • Allowed: This option allows you to perform the operation part of the client's transaction, if any; otherwise, WCF will create a new transaction.
  • NotAllowed: Unlike the previous option, NotAllowed always reject any transaction created by the customer, making the WCF always create a new transaction. This is the default value.
  • Mandatory: As the name implies, this option forces the client to create a transaction and propagate it to the service as part of the request. If not, an exception will be thrown.
The following code displays the same contract created above ( IUsuarios ), but with appropriate changes to configure the propagation of the transaction to the customer service. For purposes of example, let us define asMandatory , forcing the client to always create the transaction:
using System;
using System.ServiceModel;

[ServiceContract]
public interface IUsuarios
{
    [OperationContract]
    [TransactionFlow (TransactionFlowOption.Mandatory)]
    bool Add (string name);

    [OperationContract]
    string [] Retrieve ();
}

By defining at least one of the operations as Mandatory , while charging the WCF service will analyze whether thebinding is allowing the propagation of the transaction. If not, an exception of type InvalidOperationException will be thrown, stating that this is not allowed. The bindings that support propagation of transaction provide a property called TransactionFlow that receives a boolean value (which by default is False ) indicating whether or not this will be allowed. Moreover, it is important to say that when this property is set to True , reflected in the service's WSDL document. As we are talking about bindings is important to mention that, in addition to determining whether or not to propagate the transaction, we can define the protocol that he should use. Obviously not all protocols can be used by all bindings ., precisely because the scope of use of each Bindings as NetTcpBinding andNetNamedPipeBinding support both WS-AT protocol or OleTx ; since the bindings of internet ( WSHttpBinding ,WSDualHttpBinding and WSFederationHttpBinding ) can only expose the transactions through the WS-AT protocol.For the record, the binding BasicHttpBinding does not support transactions. Because the bindings NetTcpBindingand NetNamedPipeBinding bearing two different protocols, they expose a property called TransactionProtocol , where we can define which protocols to use. To make this setting, just choose one of the static class proriedadesTransactionProtocol , which returns an instance of that class, properly configured for that protocol. The code below illustrates the configuration of a host , displaying the customization for the transaction support:





using (ServiceHost host = 
    new ServiceHost (typeof (ServicoDeUsuarios), 
    new Uri [] {new Uri ("net.tcp: // localhost: 9393")}))
{
    NetTcpBinding tcp = new NetTcpBinding ();
    tcp.TransactionFlow = true;
    tcp.TransactionProtocol = TransactionProtocol.WSAtomicTransactionOctober2004;

    host.AddServiceEndpoint (
        typeof (IUsuarios) 
        tcp, 
        "Srv");

    host.AddServiceEndpoint (
        typeof (IMetadataExchange) 
        MetadataExchangeBindings.CreateMexTcpBinding () 
        "Mex");

    host.Open ();
    Console.ReadLine ();
}

With this configuration, the client will be required to create a transaction before actually invoking the operation and if he does not report, an exception of type ProtocolException will be triggered. The above configuration is done in the imperative form, but it is perfectly possible to do the same setup in a declarative way. When the customer is also .NET / WCF, we use the class TransactionScope to create a transactional block and, thus, to make the call of an operation within the transaction block, it will automatically be propagated to the service. The following example illustrates how the surface can proceed to create client-side transaction. Note that no extra configuration is required.

using System;
using System.Transactions;

using (proxy = new UsuariosClient UsuariosClient ())
{
    using (TransactionScope scope = new TransactionScope ())
    {
        Console.WriteLine (proxy.Adicionar ("Israel"));
        scope.Complete ();
    }
}

Note: When the service is referenced by Visual Studio or when using the utility svcutil.exe to generate the proxy , the property TransactionFlow the binding is already set to True and the contract will have the attributeTransactionFlowAttribute defined respecting the same service configuration and all this will ensure that the customer spread (or not) the transaction for the service. Operations and Management Instances One of the great challenges of transactions is precisely maintain the consistency of data it handles between the beginning and the end of it. The manipulated information ranging from data on (more conventional) database to memory variables and, in both cases, it is necessary that they are in consistent format. Choosing the management mode instances (discussed in this article ) dramatically influences on the behavior of transactions. The way PerCall is most ideal to support them, since the transaction will be completed when the service instance is finished. ModePerCall for each invocation of a (transacted or otherwise) operation, a new instance is created to serve it, keeping the initial consistency. After the return of the method and before deactivating the instance, WCF will finalize the transaction, making the commint or abort . Managing the transaction starts to get a little more complicated when the service is exposed through the way PerSession (which is the default ), causing an instance survives between calls. Once the client makes a connection to a service of this type, the instance will live while the client instance (proxy ) exists. Extra care should be taken in this scenario, due to the fact that it may break two of the characteristics of transactions:. Consistency and isolation Consistency can be affected by the fact that the method can change the internal state of the class members or any other resource involved in the transaction and if the transaction is not explicitly closed, the data is in a format in-doubt will be available, and any other transaction can access it, thus breaking the second characteristic, the isolation. One way that we have to maintain a servicePerSession to ensure that transactions will function as intended, is setting the propertyReleaseServiceInstanceOnTransactionComplete attribute ServiceBehaviorAttribute to True (which is already the default), as shown below:











using System;
using System.ServiceModel;
using System.Transactions;

[ServiceBehavior (
    TransactionTimeout = "00:02:00"
    , TransactionIsolationLevel = IsolationLevel.ReadCommitted
    , ReleaseServiceInstanceOnTransactionComplete = true
    , InstanceContextMode = InstanceContextMode.PerSession
    , ConcurrencyMode = ConcurrencyMode.Single)]
public class ServicoDeUsuarios: IUsuarios
{
    [OperationBehavior (TransactionScopeRequired = true)]
    public bool Add (string name)
    {
        // Implementation
    }
}

The use of the property ReleaseServiceInstanceOnTransactionComplete forces the service to have at least one operation with the property TransactionScopeRequired set to True , and remembering that you should vote if the transaction gave certou or not, using the techniques we have already mentioned above. This will make the WCF silently discard the object, without reflecting anything to the client. This functionality is similar to JIT Just-In-Time, provided by COM +. Another consistency that will be made ​​on the mode of competition (already discussedin this article ), forcing us to define it as male , to prevent access to multi-threading from the same customer.still talking about how PerSession , the WCF provides another resource to handle transactions, which is completely independent of the property ReleaseServiceInstanceOnTransactionComplete . This mode allows the client to create a transaction for it to last as long as the session is active, ie, the session is transacted. The idea here is the transaction not be completed within the service, since the WCF dismiss the proceedings. To avoid this, we can set the property TransactionAutoComplete to False and through the attribute ServiceBehaviorAttribute , define the property TransactionAutoCompleteOnSessionClose as True that, at the end of the session, complete the transaction. Only one should be alert to the timeout , because the transaction is subject to being aborted so if this is exceeded. The example below illustrates this configuration:



using System;
using System.ServiceModel;

[ServiceBehavior (TransactionAutoCompleteOnSessionClose = true)]
public class ServicoDeUsuarios: IUsuarios
{
    [OperationBehavior (TransactionScopeRequired = true, TransactionAutoComplete = false)]
    public bool Add (string name)
    {
        // Implementation
    }
}

Finally, the mode Single instance management also has its peculiarities. Services exposed in this way has a behavior similar to the way PerCall . As discussed above, the default value of the propertyReleaseServiceInstanceOnTransactionComplete is True , and with it, after a transaction, the service instance is discarded to maintain data consistency. This behavior will cause the service to manage the state every call and, technically speaking, a client identifier must be passed so that the service can retrieve possible information.Conclusions: The article demonstrated the various configurations supported by WCF to create and manage transactions. Was spoken since the need of transactions through the necessary infrastructure and finally talking about how to configure it in exposed via WCF services. We note that the WCF fully decouples the creation (and sometimes managing) the transactions of the service implementation, causing the developer to concentrate on the business rule, because the transaction will be easily enabled.

Comments

Popular posts from this blog

WCF - Throttling and Pooling

WCF - Managing Instances

WCF - asynchronous calls