在我们的项目中,我们使用TransactionScope来确保我们的数据访问层在事务中执行它的操作。我们的目标是不要求在最终用户的机器上启用MSDTC服务。
问题是,在一半的开发人员机器上,我们可以在禁用MSDTC的情况下运行。另一半必须启用它,否则他们会得到“MSDTC on [SERVER] is unavailable”错误消息。
这真的让我摸不着头脑,并让我认真考虑回到一个基于ADO的自制的类似transactionscope的解决方案。NET事务对象。这看起来很疯狂——同样的代码在我们的开发人员的一半上运行(并且没有升级),却在另一个开发人员的代码上升级。
我希望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 Dev 2: Windows 7 x86 SQL2008 Dev 3: Windows 7 x64 SQL2005 SQL2008
不适用的开发人员:
Dev 4: Windows 7 x64, SQL2008 SQL2005 开发5:Windows Vista x86, SQL2005 Dev 6: Windows XP X86, SQL2005 我的家庭电脑:Windows Vista家庭高级版,x86, SQL2005
我应该补充一点,为了找到问题,所有的机器都已经用微软更新提供的所有东西打了完整的补丁。
更新1:
http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/ describes a similar problem...back in 2006! http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - read that code sample, it clearly demonstrates a nested-second connection (to a second SQL server, actually) which will escalate to DTC. We are not doing this in our code - we're not using different SQL servers, nor different connection strings, nor do we have nested secondary connections opening - there should not be escalation to DTC. http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx (from 2005) talks about how escalation to DTC will always happen when connecting to SQL2000. We're using SQL2005/2008 http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN on transaction escalation.
该MSDN事务升级页面指出,以下条件将导致事务升级到DTC:
At least one durable resource that does not support single-phase notifications is enlisted in the transaction. At least two durable resources that support single-phase notifications are enlisted in the transaction. For example, enlisting a single connection with does not cause a transaction to be promoted. However, whenever you open a second connection to a database causing the database to enlist, the System.Transactions infrastructure detects that it is the second durable resource in the transaction, and escalates it to an MSDTC transaction. A request to "marshal" the transaction to a different application domain or different process is invoked. For example, the serialization of the transaction object across an application domain boundary. The transaction object is marshaled-by-value, meaning that any attempt to pass it across an application domain boundary (even in the same process) results in serialization of the transaction object. You can pass the transaction objects by making a call on a remote method that takes a Transaction as a parameter or you can try to access a remote transactional-serviced component. This serializes the transaction object and results in an escalation, as when a transaction is serialized across an application domain. It is being distributed and the local transaction manager is no longer adequate.
我们没有经历第三种情况。#2没有发生,因为一次只有一个连接,而且它也是连接到单个“持久资源”。有可能发生第一条吗?某些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:如果一个额外的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将打开和关闭连接,有效地为您完成事务处理,因为您现在不能重新打开它。
因此,基本上,为了成功地将TransactionScope与SQL2005一起使用,您需要某种全局连接对象,该对象从实例化第一个TransactionScope开始一直保持打开状态,直到不再需要为止。除了全局连接对象的代码气味之外,首先打开连接并最后关闭它与尽可能晚打开连接和尽快关闭连接的逻辑是不一致的。