"MS DTC has cancelled the distributed transaction" SQLCLR 存储过程中的错误

"MS DTC has cancelled the distributed transaction" error in SQLCLR stored procedure

我需要将一些代码从旧项目移到新项目。旧项目使用带存储过程的 DLL(32 位版本),但我需要在 64 位 SQL 服务器上使用此 DLL,因此我需要重写这些程序。

我正在为 SQL Server 2008 编写带有存储过程的 dll。在 Management Studio 中,我加载程序集,然后使用以下方法创建过程:

CREATE PROCEDURE ...
AS EXTERNAL NAME 

旧的 DLL 程序只是使用到远程 SQL 服务器的新连接到其上的 运行 存储过程和 return 结果。

所以在我的过程中,我创建了一个 SqlConnection 到远程服务器,并在远程服务器上创建了 运行 存储过程:

using (SqlConnection connection = new SqlConnection(String.Format("User ID={0};Password={1};Persist Security Info=True;Initial Catalog={2};Data Source={3}", Login, Password, DBName, ServerName)))
{
    connection.Open();
    SqlCommand command = new SqlCommand("Exec ProcName", connection);
    SqlDataReader reader = command.ExecuteReader();
    SqlContext.Pipe.Send(reader);
}

如果我 运行 在 SSMS 中使用这个程序,它就可以工作。但是在旧项目中它会引发错误:

The Microsoft Distributed Transaction Coordinator (MS DTC) has cancelled the distributed transaction.

MSDTC 服务 运行s,我设置了所有安全参数。如何解决?也许还有其他方法可以 运行 远程存储过程(链接服务器),但我需要保存旧项目功能。

这里有几件事情不太对劲:

  1. 你为什么要重写任何东西?如果您有代码,最坏的情况就是为新架构重新编译。

  2. 你一开始为什么要做什么?代码应该针对 "Any CPU"(在 "Project Properties" 的 "SQLCLR Build" 选项卡中的 "Platform target" 下)编译,而不是专门针对 32 位或 64 位。如果它已经在 "Any CPU" 下编译,则无事可做。在开始任何重新编译 and/or 重写之前,您是否在新系统上进行了测试?

  3. 不要使用 String.Format 创建连接字符串。相反,使用 SqlConnectionStringBuilder:

    SqlConnectionStringBuilder _ConnectionStringBuilder =
                                     new SqlConnectionStringBuilder();
    
    _ConnectionStringBuilder.DataSource = ServerName;
    _ConnectionStringBuilder.InitialCatalog = DBName;
    _ConnectionStringBuilder.UserID = Login;
    _ConnectionStringBuilder.Password = Password;
    
  4. 除非你别无选择,必须使用这个选项,否则不要指定Persist Security Info=True;

  5. 而不是使用 new SqlCommand(),创建 SqlCommand 使用:

    using(SqlCommand command = connection.CreateCommand())
    {
      command.CommandText = "Exec ProcName";
    }
    
  6. 一定要指定 command.CommandType = CommandType.StoredProcedure; 以便它执行实际的 RPC 调用而不是临时查询。这将要求您从 "EXEC ProcName" 的当前 CommandText 中删除 "EXEC" 的文本;您只能指定 [[DatabaseName.]SchemaName.]ProcName.

  7. 一个SqlDataReader是一个一次性对象,就像SqlConnectionSqlCommand一样,所以SqlDataReader reader = command.ExecuteReader()应该被包裹在一个[=23中=]构造.

更正上述项目后,您应该能够通过简单地设置 SqlConnectionStringBuilder 的以下 属性 来修复错误:_ConnectionStringBuilder.Enlist = false.

有关使用 SQLCLR 的更多详细信息和示例,请参阅我在 SQL Server Central 上就此主题撰写的系列文章:Stairway to SQLCLR(免费注册需要阅读该网站上的内容)。