Introduction
I am sure that Transaction is not a new concept for you but you want to see how WCF can do this. Here, this mini article touches some peripheral of WCF transactions. This is for people who have some understanding of WCF and want some start up of this concept. It gives some basic idea about how stuff works and prepares a platform for further reading. Transaction is a huge concept and cannot be explained in a single article.
Using the Code
It will be good if you download the attached code and configure it at your machine before you start reading further. The article makes changes to the code and explains the effect, so it will be more explanatory if reading goes with practicals.
Explanation
What is a transaction?
Though we understand, still to recapitulate – A Transaction implies a set of operations which complies to ACID.
- Atomic- Either all operations will make an impact on the system or none of them.
- Consistency – System will always be in consistent state irrespective of operation set.
- Isolation – Intermediate state of system will not be exposed to the outer world.
- Durability – System’s state will be persisted and will not be volatile.
In general, basically two types of transactions can be encountered in WCF– One when Client starts some transaction, performs some work and propagates it to the server to get some work done in it and another when Client does not start any transaction but requires server to perform some action in some transaction.
When transaction does not cross its Appdomain
’s boundary, it is local else it can be treated as distributed. WCF provides rich support to handle these transactions which we will see shortly.
Transaction is a huge topic to discuss and cannot be covered in a small article like this, but it will provide a brief idea about how to start playing with it.
How to Configure Transaction in WCF?
When we analyse some logic to be critical enough worthy to be wrapped into transaction (like account credit-debit), first it should be identified which portion of logic- service side or client side or both, requires a transaction. Transaction can be required inside service only or it may be initiated at client and then propagated to service or may be at client only. This decision will be helpful while actually configuring the transaction.
There are some factors that need to be considered while configuring a transaction in WCF-
Binding - There are only few bindings in WCF that can support transaction, these are NetTcpBinding
, NetNamedPipeBinding
, WSHttpBinding
, WSDualHttpBinding
, and WSFederationHttpBinding
. So, in order to configure transaction, one has to make a choice out of such bindings. Though these bindings do have transaction support but by default it is disabled, so one has to make them enabled.
Operation Contract and behavior - An appropriate binding merely makes a way to propagate transaction from client to service but at service there is only an operation/method that is going to take care of that. So, the next step is to configure an operation. Operations are configured generally with the following two attributes:
A) TransactionFlow
– This attribute is set with a parameter named TransactionFlowOption
which has three values. Its values are self descriptive:
- Allowed – Implies if client has transaction, it will be accepted otherwise not a problem.
- NotAllowed – Client cannot send transaction to server while calling a method.
- Mandatory – Client has to create a transaction otherwise the method cannot be called.
B) TransactionScopeRequired
- This OperationBehavior
depicts whether the method/operation should do its work inside a transaction.
Now let’s put it together, there is some logic that should be in some transaction. WCF has few bindings which support transactions, but by default this is disabled. To enable transaction's flow from client to service, binding support should be enabled. After binding support is there, configure method to accept transaction from client by setting TransactionFlow
attribute. At last, configure TransactionScopeRequired
so that service method could work in a transaction.
Let’s make a different configuration and see what happens.
Before we dive into experiment, I tell you that in the attached sample, we have 1 simple service which has least configuration making it just work, 1 client WinForm application with button click and 1 simple emp
table in database. Now we do some experiment to see what happens when we do something.
- In the first attempt, we will just decorate the
upload
method with TransactionScopeRequired = false
. The code will look like:
[OperationBehavior (TransactionScopeRequired=false)]
public void upload()
{
}
Now run the application (in release mode by Ctrl + F5), though you will see some error still the Emp
table will get some data. Emp
table got some data because the method was not in some transaction. This time, just make this attribute true
and see what happens. Your code will look like this:
[OperationBehavior(TransactionScopeRequired=true)]
public void upload()
{
}
This time, the database will not get new entry. This is because the method runs under some transaction which commits only if there is no error in the code execution. Here, we have just decorated this method with “TransactionScopeRequired
” attribute and WCF takes care of the rest. If WCF does not find any exception during execution, it just commits the transaction and does not bother about any other thing. We can control this default behavior by another attribute “TransactionAutoComplete
”. By default, its value is true
but if it is specified as false
, WCF is not going to commit transaction even if there is no exception in the code, and then you will have to commit it yourself.
Just make your code look like this:
[OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=false )]
public void upload()
{
}
You will have to decorate your service contract by the following attribute as shown below. This is because TransactionAutoComplete=false
is only possible with sessionful call:
[ServiceContract(SessionMode = SessionMode.Required)]
public interface Iservice
{
}
Just comment the line which raises exception in service method and run the code. Though you will not get any exception, still database does not get any new entry. This is because WCF is not working in default manner and leaves upto you to commit your transaction. Now write the following (after the line you just commented) to explicitly commit the transaction:
OperationContext.Current.SetTransactionComplete();
Run the code, this time your database will get new entries. This is because you are explicitly committing the transaction.
Let us conclude this. So far, we have played with a method by decorating with two attributes named TransactionScopeRequired
and TransactionAutoComplete
. TransactionScopeRequired
specifies whether the method will be executed in some transaction or not while another attribute TransactionAutoComplete
specifies whether WCF can commit transaction if there is no error or programmer has to commit the transaction.
In the next section, we will propagate transaction from client to service.
Previously, we observed that if configured with “TransactionScoperequired
”, the method at the service runs under some transaction and this transaction was created in service method itself. So far so good, but what if there is some code at client which works in conjunction with service and expects to be committed as a single unit (client logic + server logic). In such a situation, a transaction will be required at client where client will do its work under this transaction and then will pass it to the service where service will do its work under that and if everything is ok only then it will be committed at client. To make transaction’s flow from client to service, some configuration is required.
- Binding configuration – Because only few bindings in WCF support transaction flow.
- Operation behavior - We need to tell the operation/method how to react with transaction.
Let’s see how to make use of it. Unzip the fresh code again or make undo all the changes made so far and then open config file of service and add bindingConfiguration="newbinding"
to endpoint and correspondingly specify binding configuration:
<bindings>
<wsHttpBinding>
<binding name="newbinding" transactionFlow ="True" />
</wsHttpBinding>
</bindings>
Since we have already transaction aware binding, so there is no need to change the binding. Enable Operation contract to accept client's transaction by TransactionFlow
attribute as shown below:
[ServiceContract]
public interface Iservice
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void upload();
}
Make sure that service method is decorated with TransactionScopeRequired
as shown below:
[OperationBehavior(TransactionScopeRequired = true)]
public void upload()
{
}
Generate new proxy and update the client with newer service class and app.config.
At client, add reference to System.Transaction
and change button click event handler to look like:
try
{
using(TransactionScope scope = new TransactionScope())
{
IserviceClient client = new IserviceClient();
client.upload();
client.upload();
scope.Complete();
MessageBox.Show("All is well!!!");
}
}
catch
{
MessageBox.Show("Some error occurred...");
}
Notice the code, we have surrounded the logic by Transaction scope. This transaction scope flows to the service and there service code runs under this transaction. Here scope.complete()
is responsible to commit the transaction. If you remove this line, transaction will not be committed and no changes will be preserved to database.
If you swap second client.upload()
and scope.complete()
in the above mentioned code, then the second service call will have no effect. Similarly, you can play with different combinations of TransactionFlow
, TransactionScopeRequired
, TransactionAutoComplete
, etc. attributes and learn more.
That's all for now. Let's do a quick revision:
- In WCF, only few findings support transactions.
- By default, transactions flow is disabled in such bindings.
TransactionFlow
and TransactionScopeRequired
attributes are required to configure service operation/method.- Auto commit behavior of WCF can be controlled by
TransactionAutoComplete
attribute.
Hope this small article has given you some idea about WCF transaction and now you will be in a position to experiment yourself. I would suggest MSDN for in-depth analysis of the concept.
Though I have tried to keep this stuff error free, please let me know if something is missing or needs correction as it will be helpful for all of us.
Wishing all the best for further reading.
Happy programming.