The COM+ Integration and WS-AtomicTransaction hot fix package for Indigo is now available and I thought that it would be good to give it a little exercise…
I’ll take a quick walk through the steps required to get a transactional client (using the very sweet System.Transactions) talking to “transactions required” COM+ component using an Indigo transport.
Prepare your machines...
Having installed Indigo from the WinFX Beta 1 RC, you should download and install the hotfix for your platform and language from http://go.microsoft.com/fwlink/?linkid=46976. If you intend to use transactions with Indigo and / or Indigo’s COM+ integration feature, this is a MUST have.
You then need to enable the WS-AtomicTransactions protocol in the Distributed Transaction Coordinator (MSDTC) service. Use the “xws_reg -wsat+” command to do this – chances are that this is living in the directory %WINDOWS%\Microsoft.NET\Framework\v2.0.50215.
Rev your server-side code...
Rather than starting from scratch, the code will be a modification of the “Integrating with a COM+ Application as an Indigo Service” from the WinFX SDK – install the SDK for the full sample solution. This gives an Enterprise Services / COM+ component and to turn it into the component that we need we can just add the TransactionAttribute set to TransactionOption.Required:
[Guid("BE62FF5B-8B53-476b-A385-0F66043049F6")]
[ProgId("ServiceModelSample.esCalculator")]
[System.EnterpriseServices.TransactionAttribute( System.EnterpriseServices.TransactionOption.Required)]
// Supporting implementation for the ICalculator interface.
public class esCalculatorService : ServicedComponent, ICalculator
Clearly in a more realistic example I would present some code that hooked up with a Northwind database and performed some actual transacted resource work but the effect is much the same.
Having made the component transactional, you should build it, gac it, regsvcs it and ComSvcConfig it, as described in the WinFX sample documentation. The ComSvcConfig tool will recognize that the underlying app is transactional and will provide an appropriate transactional binding.
At this point you should have a full Indigo service, fronting a COM+ app, ready and able to receive those web service transactions.
Rev your client-side code...
Again, you can modify the code from “Integrating with a COM+ Application as an Indigo Service”. You need to update the binding to support flowing transactions and then modify the client code to create a transaction.
Edit the client App.config file so that we define and use a new binding configuration. As you can see this is really just enabling a flowTransactions attribute:
<system.serviceModel>
<client>
<endpoint configurationName="CalculatorEndpoint"
address="http://localhost/ServiceModelSamples/BE62FF5B-8B53-476B-A385-0F66043049F6.svc/C551FBA9-E3AA-4272-8C2A-84BD8D290AC7"
bindingSectionName="wsProfileBinding"
bindingConfiguration="comTransactionalBinding"
contractType="ICalculator" />
</client>
<bindings>
<wsProfileBinding>
<binding configurationName="comNonTransactionalBinding" reliableSessionEnabled="true" />
<binding configurationName="comTransactionalBinding" flowTransactions="Allowed" reliableSessionEnabled="true" />
</wsProfileBinding>
</bindings>
</system.serviceModel>
For the client code modification, you need to add the namespace and reference (using System.Transactions) then modify the Main. The modified code below will start a new transaction scope of the require type then call the Add operation automatically flowing the transaction as directed by the config file. It will pause for a little breather and then, within the same transaction scope and if all went well, then the transaction will commit (or it would if it had done any useful work).
static void Main()
{
// Start executing within a new transaction
Console.WriteLine("Creating transaction scope");
TransactionOptions tOpt = new TransactionOptions();
using (TransactionScope tx = new TransactionScope(TransactionScopeOption.RequiresNew, tOpt, EnterpriseServicesInteropOption.Full))
{
// Create a proxy with given client endpoint configuration
using (CalculatorProxy proxy = new CalculatorProxy("CalculatorEndpoint"))
{
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = proxy.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
// Take a breather half way through...
// You can see the transaction in the server's DTC Transaction List
Console.WriteLine("\nPerformed within transaction {0}",
Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());
Console.WriteLine("Press <ENTER> to continue");
Console.ReadLine();
// Call the Subtract service operation.
value1 = 145.00D;
value2 = 76.54D;
result = proxy.Subtract(value1, value2);
Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);
// Complete the transaction
Console.WriteLine("\nCommitting transaction {0}",
Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());
tx.Complete();
// Close the proxy.
proxy.Close();
}
}
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
As the inline comment says, do check the DTC Transaction List to validate that a transaction is underway.
Things to note:
- Having created a transactional COM+ component, there was no code to write to enable Indigo or WS-AtomicTransaction support
- The server is entirely decoupled from client implementation choices – if the client can flow a web service transaction, they can coordinate a COM+ app. This is new.
- Having set the client config to flowTransactions there was no explicit code required to package up that transaction into message headers – I’m sure that those WS-AT headers are fascinating and all but I don’t want to need to know them
- No DCOM required – tell that to your firewall admin!
As always, feedback welcome.