Within C#, I have an OracleConnection that updates the DB, and a reference to a legacy VB6 DLL which the C# calls into to update the DB (within the DLL, it uses an ADODB.Connection object).
I need to wrap them both in one big transaction, so that both the managed and unmanaged updates rollback or commit together.
I tried switching the C# class so that it inherits from System.EnterpriseServices.ServicedComponent and is decorated with [Transaction(TransactionOption.Required)], and then using [AutoComplete] on the method that starts the calling sequence which eventually hits the OracleConnection and VB6 DLL invocation.
Like this:
using System.EnterpriseServices;
{
[Transaction(TransactionOption.Required)]
public class MyClassTx: ServicedComponent
{
private MyClass1 _myClass1;
public MyClassTx()
{
}
// This method automatically commits the transaction if it succeeds.
[AutoComplete]
public void DoStuffTransactionally()
{
// Calls into different objects, doing some work that I'd like to have
// a big transaction around.
_MyClass1 = new MyClass1()
_MyClass1.DoSomeStuff();
}
}
}
However, when my test harness tries to instantiate MyClassTx, I get this error:
{System.EnterpriseServices.RegistrationException: Invalid ServicedComponent-derived classes were found in the assembly.
(Classes must be public, concrete, have a public default constructor, and meet all other ComVisibility requirements)
I have verified that my class is public, concrete, and has a parameter-less constructor. Still, it won't instantiate.
Do I need to strong type my assembly and put it into a COM+ package before I can even debug it? I would have assumed that, using VS2010, I could just step into the ServicedComponent-inheriting code.
It's been about 8 years since I have used COM+, and this is my first time trying to get it to work in C#, so any help would be greatly appreciated!
Also, if I am heading down a silly path here and there is an easier way to get my managed and unamanaged code into the same Transaction, please enlighten me!
A few of hours with Google hasn't helped much.
Many thanks!!
Okay, I think I have made significant progress with this.
Additional steps that I needed to do to make this all work:
You have to call OracleConnection.EnlistDistributedTransaction, passing in ContextUtil.Transaction (cast it as a Transaction) like this:
connection.EnlistDistributedTransaction((ITransaction)ContextUtil.Transaction);
After this, the assembly was showing up in the COM+ Applications listing in the Component Services window. Yay!
Even better, when I debugged in VS2010, when I got into the DoStuffTransactionally method, a new transaction was active in the Component Services explorer.
So, that let me do my debugging.
However, to actually get the Transaction including not just the managed code but also the legacy VB6 code being invoked deeper inside DoStuffTransactionally, I needed to add the legacy VB6 COM objects to the COM+ Application that my managed code was in. And since this VB6 code was invoking Oracle, I had to modify the Connection String the VB6 code was using to have DistribTX=1 and PROMOTABLE TRANSACTION=PROMOTABLE set. After that, the code was committing and rolling back the managed & unmanaged DB changes as a single Transaction.
Yay!
I do get an error thrown at the end of my debugging session, which doesn't make sense to me. Hopefully it only affects debugging and not Release code. The error is:
So, I hope this helps someone someday.