托管和未修改的代码在一个事务中更新数据库?

发布于 2024-10-13 01:02:34 字数 1574 浏览 9 评论 0原文

在 C# 中,我有一个更新数据库的 OracleConnection,以及对旧版 VB6 DLL 的引用,C# 调用该 DLL 来更新数据库(在 DLL 中,它使用 ADODB.Connection 对象)。

我需要将它们包装在一个大事务中,以便托管和非托管更新一起回滚或提交。

我尝试切换 C# 类,使其继承自 System.EnterpriseServices.ServicedComponent 并用 [Transaction(TransactionOption.Required)] 装饰,然后在启动调用序列的方法上使用 [AutoComplete],最终命中 OracleConnection 和 VB6 DLL 调用。

像这样:

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();
        }
    }
}

但是,当我的测试工具尝试实例化 MyClassTx 时,我收到此错误:

{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)

我已验证我的类是公共的、具体的,并且具有无参数构造函数。尽管如此,它仍然不会实例化。

我是否需要对程序集进行强类型化并将其放入 COM+ 包中,然后才能对其进行调试?我本以为,使用 VS2010,我可以直接进入 ServicedComponent 继承代码。

我使用 COM+ 已经有大约 8 年了,这是我第一次尝试让它在 C# 中工作,所以任何帮助将不胜感激!

另外,如果我在这里走上了一条愚蠢的道路,并且有一种更简单的方法可以将我的托管和非托管代码放入同一个事务中,请启发我!

在谷歌上呆了几个小时并没有多大帮助。

非常感谢!

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!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

黑寡妇 2024-10-20 01:02:34

好吧,我想我在这方面已经取得了重大进展。

为了使这一切顺利进行,我需要执行其他步骤:

  1. 必须将程序集设置为 ComVisible。
  2. 我必须设置 [程序集:System.EnterpriseServices.ApplicationName("blahblah")] 值...blahblah 成为 COM+ 包的名称。
  3. 该程序集必须具有强名称,并使用 regsvcs.exe 在 COM+ 中注册。它设置为使用库激活,但我不完全确定这是否有必要。我尝试将其作为服务器激活,但调用它的代码引发了某种 COM 异常。
  4. 您必须调用 OracleConnection.EnlistDistributedTransaction,传入 ContextUtil.Transaction(将其转换为事务),如下所示:

    connection.EnlistDistributedTransaction((ITransaction)ContextUtil.Transaction);

此后,该程序集将显示在“组件服务”窗口的 COM+ 应用程序列表中。耶!

更好的是,当我在 VS2010 中调试时,当我进入 DoStuffTransactionally 方法时,组件服务资源管理器中的一个新事务处于活动状态。

所以,这让我可以进行调试。

然而,为了真正获得事务,不仅包括托管代码,还包括在 DoStuffTransactionally 内部更深入调用的旧版 VB6 代码,我需要将旧版 VB6 COM 对象添加到我的托管代码所在的 COM+ 应用程序中。并且由于此 VB6 代码在调用 Oracle 时,我必须修改 VB6 代码使用的连接字符串来设置 DistribTX=1 和 PROMOTABLE TRANSACTION=PROMOTABLE。之后,代码正在提交和回滚托管和回滚。非托管数据库更改作为单个事务。

耶!

我确实在调试会话结束时抛出了一个错误,这对我来说没有意义。希望它只影响调试而不影响发布代码。错误是:

DisconnectedContext was detected
Message: Context 0x4452b0' is disconnected.  Releasing the interfaces from the current context (context 0x444fd0). This may cause corruption or data loss. To avoid this problem, please ensure that all contexts/apartments stay alive until the application is completely done with the RuntimeCallableWrappers that represent COM components that live inside them.

所以,我希望这有一天能对某人有所帮助。

Okay, I think I have made significant progress with this.

Additional steps that I needed to do to make this all work:

  1. Assembly had to be set as ComVisible.
  2. I had to set the [assembly: System.EnterpriseServices.ApplicationName("blahblah")] value... blahblah becomes the name of the COM+ package.
  3. The assembly had to be strong-named, and the registered in COM+ using regsvcs.exe. It's set to use Library activation, but I'm not entirely sure if that's necessary. I tried it as Server activation, but the code invoking it threw a COM exception of some kind.
  4. 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:

DisconnectedContext was detected
Message: Context 0x4452b0' is disconnected.  Releasing the interfaces from the current context (context 0x444fd0). This may cause corruption or data loss. To avoid this problem, please ensure that all contexts/apartments stay alive until the application is completely done with the RuntimeCallableWrappers that represent COM components that live inside them.

So, I hope this helps someone someday.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文