c# - 托管和非托管代码在一个事务中更新数据库?

标签 c# transactions com+ mts

在 C# 中,我有一个更新数据库的 OracleConnection,以及对 C# 调用以更新数据库的遗留 VB6 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)

我已经验证我的类是公共(public)的、具体的,并且有一个无参数的构造函数。不过,它不会实例化。

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

我使用 COM+ 已经大约 8 年了,这是我第一次尝试让它在 C# 中工作,因此非常感谢任何帮助!

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

使用 Google 几个小时并没有太大帮助。

非常感谢!!

最佳答案

好的,我认为我在这方面取得了重大进展。

我需要执行的其他步骤才能使这一切正常进行:

  1. 程序集必须设置为 ComVisible。
  2. 我必须设置 [assembly: 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。之后,代码将托管和非托管数据库更改作为单个事务提交和回滚。

耶!

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

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.

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

关于c# - 托管和非托管代码在一个事务中更新数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4740020/

相关文章:

sql-server - COM+ VB6 应用程序 : RM_ENLIST_FAILED_TOO_MANY_ENLISTS error

c# - 如何读取 COM+ 服务器中的配置文件

c# - 如何初始化为空的 IEnumerable<Object> 并允许对其进行 Concat?

c# windows service process.start 收藏

sql-server - 如何在 sql azure 中将 SqlBulkCopy 与 ReliableSqlConnection 上的事务结合使用

c# - 环境事务中的 TransactionScope 错误不会回滚事务

java - com4j 与 jacob 从 Java 调用 COM 方法

c# - 使用 Select TagHelper 设置默认值/空值

c# - WPF 将控件的图像复制到位图

mysql - 如何在 MySQL 中使用事务来避免 "lost updates"?