System.Transactions and legacy code
System.Transaction is the latest incarnation of transactions from Microsoft. It first showed up on the scene with the release of .NET Framework 2.0. Today, however, was my first interaction with it. As with most things I do, I happened to be trying to do something that apparently no one else in the world as done (okay, probably not true, but when I ran into problems I sure didn’t find much help out there).
My goal was to create some WCF services that would expose some legacy C++ functionality. The C++ functionality was doing updates to some database tables using ADO (the old ADO, not the new ADO.NET). My approach to doing this seemed quite straight-forward to me so I naturally figured it wouldn’t take me long. I was going to create a new C++ library containing managed code that would turn around and call the legacy functions that are exported from another C++ library. My new C++ library could be compiled into a nice .NET assembly that I would reference from the C# WCF library. The WCF service in the C# library would just call the "wrapper" class in the new C++ library, which in turn would call the legacy function. Piece o’ cake, right?
Getting things built and compiled was easy. Getting things to work, however, was not. My issue was that the database updates that occurred within the legacy code did not occur within the WCF transaction that was created. So, if I had to rollback the transaction I would be screwed because the legacy updates wouldn’t get rolled back.
I tried several different approaches. I even went down the path of creating a ServicedComponent as my "wrapper" and using EnterpriseServices. It was that path that led me to the answer (or at least one answer that seems to work). The approach I ended up with is to put a "using (TransactionScope ….)" statement in my WCF operation that wraps the call to the C++ wrapper. The not-so-obvious trick, however, is that the TransactionScope constructor needs to be the one that allows me to specify EnterpriseServicesInteropOption = Full, like this:
using (TransactionScope s = new TransactionScope(Transaction.Current, new TimeSpan(0, 2, 0),
EnterpriseServicesInteropOption.Full)) {
// call the legacy component
s.Complete();
}
This made things work like a charm and a did NOT need to have the wrapper actually be a ServiceComponent.
