SQLCLR 存储过程中的“MS DTC 已取消分布式事务”错误
Posted
技术标签:
【中文标题】SQLCLR 存储过程中的“MS DTC 已取消分布式事务”错误【英文标题】:"MS DTC has cancelled the distributed transaction" error in SQLCLR stored procedure 【发布时间】:2016-04-01 09:16:54 【问题描述】:我需要将一些代码从旧项目移到新项目。旧项目使用带有存储过程(32 位版本)的 DLL,但我在 64 位 SQL Server 上需要这个 DLL,所以我需要重写这些过程。
我正在使用 SQL Server 2008 的存储过程编写 dll。在 Management Studio 中,我加载程序集,然后使用以下方法创建过程:
CREATE PROCEDURE ...
AS EXTERNAL NAME
旧的 DLL 过程只是使用与远程 SQL Server 的新连接在其上运行存储过程并返回结果。
所以在我的过程中,我为远程服务器创建了一个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 中运行此过程,它会起作用。但是在旧项目中它会引发错误:
Microsoft 分布式事务协调器 (MS DTC) 已取消分布式事务。
MSDTC 服务运行,我设置了所有安全参数。如何解决?也许还有其他方法可以运行远程存储过程(链接服务器),但我需要保存旧的项目功能。
【问题讨论】:
DTC 涉及事务发布开始事务的代码。您的存储过程做了一些非常邪恶的事情——它打开了一个新连接 服务器,这意味着由您的代码启动的任何现有事务都必须使用 DTC 升级为分布式事务。为什么不直接创建链接服务器并直接调用远程过程?为了运行该代码,您还必须放松 SQLCLR 安全性 【参考方案1】:这里有几件事不太对劲:
你为什么要重写任何东西?如果你有代码,最坏的情况就是为新架构重新编译。
你为什么要做任何事情?代码应针对“任何 CPU”(在“项目属性”的“SQLCLR 构建”选项卡中的“平台目标”下)编译,而不是专门针对 32 位或 64 位。如果它已经在“Any CPU”下编译,那么就没有什么可做的了。在开始重新编译和/或重写之前,您是否在新系统上进行了测试?
不要使用String.Format
创建连接字符串。相反,请使用SqlConnectionStringBuilder:
SqlConnectionStringBuilder _ConnectionStringBuilder =
new SqlConnectionStringBuilder();
_ConnectionStringBuilder.DataSource = ServerName;
_ConnectionStringBuilder.InitialCatalog = DBName;
_ConnectionStringBuilder.UserID = Login;
_ConnectionStringBuilder.Password = Password;
除非您绝对别无选择并且必须使用此选项,否则不要指定Persist Security Info=True;
不要使用new SqlCommand()
,而是使用以下方法创建SqlCommand
:
using(SqlCommand command = connection.CreateCommand())
command.CommandText = "Exec ProcName";
确保还指定command.CommandType = CommandType.StoredProcedure;
,以便它执行实际的 RPC 调用而不是临时查询。这将要求您从“EXEC ProcName”的当前CommandText
中删除“EXEC”的文本;你只能指定[[DatabaseName.]SchemaName.]ProcName
。
SqlDataReader
是一次性对象,就像SqlConnection
和SqlCommand
,所以SqlDataReader reader = command.ExecuteReader()
应该被包裹在using()
构造中。
一旦上述项目得到纠正,您应该能够通过简单地设置 SqlConnectionStringBuilder 的以下属性来修复错误:_ConnectionStringBuilder.Enlist = false
。
有关使用 SQLCLR 的更多详细信息和示例,请参阅我在 SQL Server Central 上就该主题撰写的系列文章:Stairway to SQLCLR(需要免费注册才能阅读该站点上的内容)。
【讨论】:
1.我只有旧 DLL 的代码,而不是整个项目。这段代码在 Pascal 上。在 XE6(到 64 位)下编译导致类型问题,我的团队决定编写新库而不是重构旧代码。 3-7 同意,谢谢!如果我使用StoredProcedure
类型,我只需从命令文本中删除 Exec
。
@cerberus 是的,很抱歉没有提到删除EXEC
,因为它只能是[[DatabaseName.]SchemaName.]ProcName
。我忘了那是在那里,因为我试图不忘记其他细节;-)。我将更新我的答案以包含该位。但是,旧代码是如何在 Pascal 中完成的?你是说德尔福XE6吗? XE6 是否编译为 .NET MSIL?根据embarcadero.com/products/delphi/faq 它没有。还是我错过了什么?
旧代码是Delphi 7项目,它使用ODS API。所以它与.net没有任何关系。我们尝试将 XE6 构建到 64 位平台,但是类型有很多错误。所以我们决定用c#写
@cerberus 那么也许我完全误解了旧项目。旧项目是作为 SQLCLR 完成的吗?我想此时不会,因为那需要.NET。我原本以为你是在更新 SQLCLR 项目而不是移植到 SQLCLR。
我有使用 ODS API 的 delphi-application 和 delphi dll(32 位)。现在我需要将此 dll 函数移植到 c# dll 并且因为 ODS 很旧,我使用 sqlclr以上是关于SQLCLR 存储过程中的“MS DTC 已取消分布式事务”错误的主要内容,如果未能解决你的问题,请参考以下文章
Visual Studio 2010 中 SQLCLR 项目中的计算列