在我们的项目中,我们使用TransactionScope来确保数据访问层在事务中执行它的操作。我们的目标是不需要在最终用户的机器上启用MSDTC服务。
问题是,在我们开发人员的一半机器上,我们可以禁用MSDTC运行。另一半必须启用它,否则他们会收到“MSDTC on [SERVER] is unavailable”错误消息。
这让我感到很困惑,并且让我认真考虑回滚到基于ADO.NET事务对象的自制TransactionScope解决方案。这似乎是疯狂的 - 在一半开发人员的同样代码上工作(并且不会升级)却会在另外一半开发人员的机器上升级。
我希望能找到更好的答案追踪事务何时会上升到DTC,但不幸的是这并没有给出答案。
这里有一小段代码示例,会导致问题,在尝试进行升级的机器上,它会在第二个connection.Open()上升级(是的,在此时没有其他连接打开。)
using (TransactionScope transactionScope = new TransactionScope() {
using (SqlConnection connection = new SqlConnection(_ConStr)) {
using (SqlCommand command = connection.CreateCommand()) {
// prep the command
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
// use the reader
connection.Close();
}
}
}
// Do other stuff here that may or may not involve enlisting
// in the ambient transaction
using (SqlConnection connection = new SqlConnection(_ConStr)) {
using (SqlCommand command = connection.CreateCommand()) {
// prep the command
connection.Open(); // Throws "MSDTC on [SERVER] is unavailable" on some...
// gets here on only half of the developer machines.
}
connection.Close();
}
transactionScope.Complete();
}
我们真的深入研究并试图找出问题所在。以下是一些关于它能够运行的机器的信息:
- Dev 1: Windows 7 x64 SQL2008
- Dev 2: Windows 7 x86 SQL2008
- Dev 3: Windows 7 x64
SQL2005SQL2008
无法运行的开发者机器:
- Dev 4: Windows 7 x64,
SQL2008SQL2005 - Dev 5: Windows Vista x86, SQL2005
- Dev 6: Windows XP X86, SQL2005
- 我的家用电脑: Windows Vista Home Premium, x86, SQL2005
需要补充的是,为了找出问题,所有机器都已经安装了来自Microsoft Update的所有可用补丁。
更新1:
- http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/描述了一个类似的问题...在2006年!
- http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - 阅读该代码示例,它清楚地演示了嵌套的二次连接(实际上是到第二个SQL服务器),这将升级到DTC。我们没有在我们的代码中执行此操作 - 我们不使用不同的SQL服务器,也不使用不同的连接字符串,也没有打开嵌套的二次连接 - 不应该升级到DTC。
- http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx(来自2005年)讨论了连接到SQL2000时如何始终发生升级到DTC的情况。我们正在使用SQL2005/2008
- http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN关于事务升级。
这个MSDN事务升级页面说明了以下条件会导致事务升级到DTC:
- 至少一个不支持单阶段通知的持久性资源已经在事务中注册。
- 至少两个支持单阶段通知的持久性资源已经在事务中注册。例如,仅注册一个连接不会导致事务被提升。但是,每当您打开第二个连接到数据库并导致数据库注册时,System.Transactions基础架构检测到它是事务中的第二个持久性资源,并将其升级为MSDTC事务。
- 调用请求“编组”事务到不同的应用程序域或不同的进程。例如,跨应用程序域边界序列化事务对象。事务对象是按值进行编组的,这意味着尝试将其通过应用程序域边界传递(即使在同一进程中)会导致事务对象的序列化。您可以通过调用以Transaction作为参数的远程方法来传递事务对象,或者可以尝试访问远程事务服务组件。这将序列化事务对象并导致升级,就像将事务序列化到应用程序域中一样。它正在分布式,本地事务管理器不再足够。
我们没有遇到#3。#2没有发生,因为一次只有一个连接,并且也只有一个“持久性资源”。是否有任何方式可以发生#1?一些SQL2005/8配置导致它不支持单阶段通知吗?
更新2:
重新调查了每个人的SQL Server版本-“Dev 3”实际上有SQL2008,“Dev 4”实际上是SQL2005。这会教训我再也不相信我的同事了。;)由于数据的这种变化,我非常确定我们已经找到了问题所在。我们的SQL2008开发人员没有遇到问题,因为SQL2008包含了大量SQL2005没有的强大功能。这也告诉我,因为我们将支持SQL2005,所以我们不能像之前一样使用TransactionScope,如果我们想要使用TransactionScope,我们需要传递单个SqlConnection对象...在SqlConnection无法轻松传递的情况下,这似乎是有问题的...它闻起来像是全局-SqlConnection实例。 Pew!
更新3
只是在这里澄清一下:
SQL2008:
允许在单个TransactionScope内使用多个连接(如上面示例代码中演示的)。
警告#1:如果这些多个SqlConnection嵌套,即同时打开两个或更多个SqlConnection,则TransactionScope将立即升级到DTC。
警告#2:如果打开了一个额外的SqlConnection到不同的“持久资源”(即:不同的SQL Server),它将立即升级到DTC。
SQL2005:
不允许在单个TransactionScope内使用多个连接,无论何时/如果第二个SqlConnection被打开,它都会升级。
更新4
为了使这个问题更加有用,也更加清晰明了,以下是如何使用单个SqlConnection
将SQL2005升级到DTC的方法:
using (TransactionScope transactionScope = new TransactionScope()) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
connection.Close();
connection.Open(); // escalates to DTC
}
}
这对我来说似乎有些问题,但如果每次调用 SqlConnection.Open()
都从连接池中获取的话,我想我可以理解。
"不过,为什么会发生这种情况呢?" 好吧,如果在打开它之前使用 SqlTableAdapter 对该连接进行操作,则 SqlTableAdapter 将打开并关闭该连接,有效地为您完成事务,因为您现在无法重新打开它。
因此,基本上,为了成功地使用 SQL2005 的 TransactionScope,您需要拥有某种全局连接对象,该对象从第一个 TransactionScope 实例化时保持打开状态,直到不再需要为止。 除了全局连接对象的代码异味外,先打开连接再最后关闭它与尽可能晚地打开连接和尽早关闭连接的逻辑相矛盾。
分布式事务管理器(MSDTC)的网络访问已被禁用。
内部异常为事务管理器已禁用其对远程/网络事务的支持0x8004D024
。该软件在许多站点都能正常工作,但只有少数站点不能正常工作。结果他们正在运行SQL2005,并且正如这个问题所指出的那样,他们必须使用MSDTC。 - axeman