Flowing transaction into a workflow using WF4
One of the things that wasn’t possible in Windows Workflow Foundation 3 was flowing transaction over a WCF service request into a workflow. We could have transactions on the client, we could have transactions on the server but they would not be able to cooperate.
Fortunately that is one the problems that Windows Workflow Foundation 4 solves for us. [:)]
In WF4 we can use the ReceiveAndSendreply template to configure a workflow as a service and accept WCF calls. That works just fine but doesn’t allow for transactions. If we want to use transactions we need to add the TransactedReceiveScope to the workflow. Move the ReceiveRequest activity into the Request part and the SendResponse activity into the Body part. Don’t forget the last part because leaving the SendResponse below the TransactedReceiveScope means things will not work and the error message will be less that helpful [:(]
This is enough to make all work done during the message receive part of a transaction. To show what is happening with transactions I am using the following code to record and return the transactional status:
using System.Text;
using System.Transactions;
public class TransactionReport
{
public static string GetReport()
{
var sb = new StringBuilder();
if (Transaction.Current == null)
{
sb.Append("No transaction");
}
else
{
sb.AppendFormat("TransactionInformation\n");
sb.AppendFormat("\tLocalIdentifier {0}\n", Transaction.Current.TransactionInformation.LocalIdentifier);
sb.AppendFormat("\tDistributedIdentifier {0}\n", Transaction.Current.TransactionInformation.DistributedIdentifier);
sb.AppendFormat("\tStatus {0}\n", Transaction.Current.TransactionInformation.Status);
sb.AppendFormat("\tIsolationLevel {0}\n", Transaction.Current.IsolationLevel);
}
return sb.ToString();
}
}
Adding a console application with a service reference with the following code:
using System;
using Client.ServiceReference1;
namespace Client
{
class Program
{
static void Main(string[] args)
{
var proxy = new ServiceClient();
var result = proxy.PostData();
Console.WriteLine(result);
Console.ReadLine();
}
}
}
Produces the following output:
No big surprises there. The only thing I am missing is the ability to control the IsolationLevel which seems to be fixed at Serializable.
Flowing transactions from the client
The first step we need to take for client transactions to flow to the service is do the service call inside of a transaction using this client code:
using System;
using System.Transactions;
using Client.ServiceReference1;
namespace Client
{
class Program
{
static void Main(string[] args)
{
using (var tx = new TransactionScope())
{
Console.WriteLine(TransactionReport.GetReport());
var proxy = new ServiceClient();
var result = proxy.PostData();
Console.WriteLine(result);
Console.WriteLine(TransactionReport.GetReport());
}
Console.ReadLine();
}
}
}
If we run the code now we get the following output:
The transaction is there on the client but DistributedIdentifier is still empty so the transaction doesn’t flow to the service yet.
Before transactions will flow we need to do two things. First of all we need a WCF binding that can flow transactions. So far we have been using the BasicHttpBinding, the default for an HTTP endpoint in the .NET 4 default configuration, which doesn’t support transactions. So we need to switch to something like WSHttpBinding that does. Next we need to enable transaction flow because it is disabled by default. All of this can be done by adding a little extra configuration to the web.config file.
<bindings>
<wsHttpBinding>
<binding transactionFlow="true"/>
</wsHttpBinding>
</bindings>
<services>
<service name="Service1">
<endpoint address=""
binding="wsHttpBinding"
contract="IService" />
</service>
</services>
With this in place all we need to do is update the client service reference and transactions will flow from the client to the service.
Keep in mind that flowing transactions across a network boundary is a very powerful but also a very dangerous concept. You might be letting someone else, from a different organization, place locks on your database. So make sure you understand the implications of flowing transactions before you open up a whole can of worms!
Nice [:)]
Enjoy!
TheProblemSolver
DotNetEvents