At the service implementation level, attributes control the transactions. For example, the
ServiceBehavior attribute offers a
TransactionIsolationLevel property that you can
use to set the isolation level. In this case, the default of
Unspecified is used. The
TransactionAutoCompleteOnSessionClose is used to indicate whether the transaction should
complete if the session ends and no errors have occurred. Again, in this case, the default of
false
is used. A final property,
TransactionTimeout, is available to set a timeout for active transactions
but not used here.
At the service operation level, the OperationBehavior attribute has two key properties:
TransactionAutocomplete indicates that a transaction should automatically complete if the operation
completes without error. The TransactionScopeRequired property tells WCF whether or not this
particular operation must be run within the scope of a transaction. A transaction may be flowed from the caller or
created specifically for an operation.
The combination of TransactionFlow and TransactionScopeRequired can
be confusing. The following table should provide clarity:
Table 1. Combining Attributes: This table helps with clarification. |
TransactionFlow | TransactionScopeRequired | Uses Flowed Transaction from Caller | Result |
Mandatory | True | Yes | Uses Flowed Transaction |
Allowed | True | Yes | Uses Flowed Transaction |
NotAllowed | True | Yes | Throws Exception |
Mandatory | False | Yes | No transaction used |
Allowed | False | Yes | No transaction used |
NotAllowed | False | Yes | Throws Exception |
Mandatory | True | No | Throws Exception |
Allowed | True | No | A new transaction is created for the operation |
NotAllowed | True | No | A new transaction is created for the operation |
Mandatory | False | No | Throws Exception |
Allowed | False | No | Uses No Transaction |
NotAllowed | False | No | Uses No Transaction |
Note that in cases where TransactionFlow is set to NotAllowed, an attempt to flow a transaction will result in an error. At first it may seem counterintuitive to allow a transaction to be flowed to a service operation that will not perform its operation within that transaction but, this capability can be quite useful. A transaction that does not utilize a flowed transaction can still vote on the outcome of the transaction. What this means is that operations can help determine the outcome of a transaction even if they themselves are not transactional.
The final step is to update the application configuration file for the service. Specifically, the Bindings element needs to be updated.
<bindings>
<wsHttpBinding>
<binding name="LogManager" transactionFlow="true" />
</wsHttpBinding>
</bindings>
In this case, use the
wsHttpBinding and tell it to allow transactions to flow via the
transactionFlow attribute. The default value for this attribute is false, so it must be
explicitly set to
true.
After the service for LIM completes, the same pattern is followed for the TMM service (see
Listing 2).
The ConstructAToothpick operation utilizes the Mandatory setting to require a flowed transaction.
Also notice that the ConstructAToothpick operation throws an exception, indicating that the machine is broken. The act of throwing the exception causes the transaction to fail. In the actual production code, the TMM API would be called.
The final phase in the development process is to create the new ACME Manufacturing Manager (AMM) application. The
AMM is a Windows Forms application with references to both the LIM and TMM services created previously
(see Listing 3).
Recall that the two services require the caller to flow a transaction. To that end, the client code creates an
instance of TransactionScope within a using block. All service calls executed within that using
block will have the transaction flowed to them. Also note that the client explicitly completes the transaction by
calling the Complete method.
The TransactionCompleted event handler was added here simply for illustrative purposes. It is not required.
While the process appears involved, further examination reveals only five steps were performed:
- Add a TransactionFlow attribute to the service contract.
- Add a ServiceBehavior attribute to the service implementations.
- Add an OperationBehavior attribute to the operation implementations.
- Update the Bindings element in the configuration file.
- Add basic transaction code to the service client.
Notice that most of the steps involved adding attributes and not writing new code. In this way, WCF abstracts the actual service implementation away from its transactional behavior.
The rollout of the new ACME Manufacturing Manager application was a resounding success. Now purchasing knows exactly how many logs are in inventory and sales knows exactly how many toothpicks are available. Thanks to WCF and its built-in support for transactions, IT built the application in record time. The savings from the new application allowed ACME to purchase a chopstick-manufacturing machine. Of course, the Chopstick Manufacturing Manager runs on a different platform. Thanks to the foresight of IT, ACME's new machine will be talking to its other systems right away.