在我们的项目中,我们使用TransactionScope来确保我们的数据访问层在事务中执行其动作。我们的目标是不要求在最终用户的计算机上启用MSDTC服务。
麻烦的是,在我们一半的开发人员机器上,我们可以在禁用MSDTC的情况下运行。另一半必须启用它,否则他们会收到“ [SERVER]上的MSDTC不可用”错误消息。
这真的让我抓狂了,还让我认真考虑回滚到基于ADO.NET事务对象的类似HomeScope的TransactionScope解决方案。这似乎是疯狂的-在我们一半的开发人员的代码上起作用(并且不会升级)的相同代码确实会在其他开发人员的代码上升级。
我希望对Trace有了一个更好的答案,为什么交易会升级为DTC,但不幸的是却没有。
这是会引起问题的示例代码,在尝试升级的机器上,它尝试在第二个连接上升级.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();
}
我们已经认真研究并试图解决这个问题。以下是适用于其的计算机的一些信息:
- 开发1:Windows 7 x64 SQL2008
- 版本2:Windows 7 x86 SQL2008
- 版本3:Windows 7 x64
SQL2005SQL2008
开发人员无法使用:
- 版本4:Windows 7 x64,
SQL2008SQL2005 - 版本5:Windows Vista x86,SQL2005
- 版本6:Windows XP X86,SQL2005
- 我的家用电脑:Windows Vista Home Premium,x86,SQL2005
我应该补充说,所有计算机都已使用Microsoft Update的所有功能进行了全面修补,以解决问题。
更新1:
- http://social.msdn.microsoft.com/forums/zh-CN/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/描述了类似的问题……早在2006年!
- http://msdn.microsoft.com/zh-cn/library/system.transactions.transactionscope%28VS.80%29.aspx-阅读该代码示例,清楚地演示了嵌套秒连接(与第二个SQL Server,实际上),这将升级为DTC。 我们没有在代码中执行此操作 -我们没有使用不同的SQL服务器,也没有使用不同的连接字符串,也没有嵌套的辅助连接打开- 不应升级为DTC。
- http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx(从2005年开始)讨论了在连接到SQL2000时如何始终升级到DTC。我们正在使用SQL2005 / 2008
- 有关事务升级的http://msdn.microsoft.com/zh-cn/library/ms229978.aspx MSDN。
该MSDN事务升级页面指出以下情况将导致事务升级到DTC:
- 事务中至少有一个不支持单阶段通知的持久资源。
- 事务中至少有两个支持单阶段通知的持久资源。例如,仅注册一个连接不会导致事务升级。但是,每当您打开与数据库的第二个连接而导致该数据库加入时,System.Transactions基础结构都会检测到它是事务中的第二个持久资源,并将其升级为MSDTC事务。
- 调用将事务“封送”到另一个应用程序域或另一个进程的请求。例如,跨应用程序域边界对事务对象进行序列化。事务对象按值封送处理,这意味着任何试图跨应用程序域边界传递该对象(即使在同一过程中)都会导致该事务对象的序列化。您可以通过调用将事务作为参数的远程方法来传递事务对象,也可以尝试访问远程事务服务组件。这会序列化事务对象,并导致升级,就像跨应用程序域序列化事务时一样。它正在分发,并且本地事务管理器不再足够。
我们没有遇到#3。#2不会发生,因为一次只有一个连接,而且也连接到一个“持久资源”。有什么办法可能发生#1?是否导致某些SQL2005 / 8配置不支持单阶段通知?
更新2:
重新调查后,个人而言,每个人的SQL Server版本-“ Dev 3”实际上具有SQL2008,而“ Dev 4”实际上是SQL2005。那将教会我永远不要再信任我的同事。;)由于数据的这种变化,我很确定我们已经找到了问题。我们的SQL2008开发人员没有遇到此问题,因为SQL2008包括SQL2005没有的大量功能。
它还告诉我,因为我们将要支持SQL2005,所以我们不能像以前那样使用TransactionScope,并且如果我们想使用TransactionScope,我们将需要传递单个SqlConnection对象。在无法轻松传递SqlConnection的情况下,这似乎是有问题的...它只是闻到global-SqlConnection实例的味道。座位!
更新3
只是为了在这里澄清问题:
SQL2008:
- 允许在单个TransactionScope中进行多个连接(如上面的示例代码所示)。
- 注意事项1:如果嵌套了多个SqlConnection,即同时打开两个或多个SqlConnection,则TransactionScope将立即升级为DTC。
- 警告#2:如果向其他“持久资源”(即:其他SQL Server)打开了额外的SqlConnection ,它将立即升级为DTC
SQL2005:
- 在单个TransactionScope期间内不允许多个连接。当/如果打开第二个SqlConnection,它将升级。
更新4
在做出这个问题更加的利益乱七八糟的有用的,只是为了更清晰起见,这里是你如何能得到SQL2005升级到DTC带有单 SqlConnection
:
using (TransactionScope transactionScope = new TransactionScope()) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
connection.Close();
connection.Open(); // escalates to DTC
}
}
这对我来说似乎很困难,但是我想我可以理解是否每个调用SqlConnection.Open()
都从连接池中获取。
“为什么会这样呢?” 好吧,如果在打开该连接之前对它使用SqlTableAdapter,则SqlTableAdapter将打开和关闭该连接,为您有效地完成了事务,因为您现在无法重新打开它。
因此,基本上,为了成功地将TransactionScope与SQL2005结合使用,您需要具有某种全局连接对象,该对象从实例化第一个TransactionScope直到不再需要它时都保持打开状态。除了全局连接对象的代码气味外,首先打开连接并最后关闭它与延迟打开连接并尽快关闭连接的逻辑是矛盾的。