建议的做法是在使用transactioncope时停止升级到分布式的事务

使用TransactionScope对象来设置不需要跨函数调用传递的隐式事务非常棒! 但是,如果连接打开而另一个连接已打开,则事务协调器会静默升级要分发的事务(需要MSDTC服务运行并占用更多资源和时间)。

所以,这很好:

using (var ts = new TransactionScope()) { using (var c = DatabaseManager.GetOpenConnection()) { // Do Work } using (var c = DatabaseManager.GetOpenConnection()) { // Do more work in same transaction using different connection } ts.Complete(); } 

但这会使交易升级:

  using (var ts = new TransactionScope()) { using (var c = DatabaseManager.GetOpenConnection()) { // Do Work using (var nestedConnection = DatabaseManager.GetOpenConnection()) { // Do more work in same transaction using different nested connection - escalated transaction to distributed } } ts.Complete(); } 

是否有建议的做法是避免以这种方式升级事务,同时仍然使用嵌套连接?

我现在能想到的最好的是拥有一个ThreadStatic连接并重新使用,如果设置了Transaction.Current,就像这样:

 public static class DatabaseManager { private const string _connectionString = "data source=.\\sql2008; initial catalog=test; integrated security=true"; [ThreadStatic] private static SqlConnection _transactionConnection; [ThreadStatic] private static int _connectionNesting; private static SqlConnection GetTransactionConnection() { if (_transactionConnection == null) { Transaction.Current.TransactionCompleted += ((s, e) => { _connectionNesting = 0; if (_transactionConnection != null) { _transactionConnection.Dispose(); _transactionConnection = null; } }); _transactionConnection = new SqlConnection(_connectionString); _transactionConnection.Disposed += ((s, e) => { if (Transaction.Current != null) { _connectionNesting--; if (_connectionNesting > 0) { // Since connection is nested and same as parent, need to keep it open as parent is not expecting it to be closed! _transactionConnection.ConnectionString = _connectionString; _transactionConnection.Open(); } else { // Can forget transaction connection and spin up a new one next time one's asked for inside this transaction _transactionConnection = null; } } }); } return _transactionConnection; } public static SqlConnection GetOpenConnection() { SqlConnection connection; if (Transaction.Current != null) { connection = GetTransactionConnection(); _connectionNesting++; } else { connection = new SqlConnection(_connectionString); } if (connection.State != ConnectionState.Open) { connection.Open(); } return connection; } } 

编辑:所以,如果答案是在它嵌套在事务管理器内时重用相同的连接,就像上面的代码那样,我想知道在事务中间处理这个连接的含义。

到目前为止,我可以看到(使用Reflector检查代码),重置连接的设置(连接字符串等),并关闭连接。 所以(理论上),重新设置连接字符串并在后续调用中打开连接应“重用”连接并防止升级(我的初始测试同意这一点)。

虽然看起来确实有点hacky ……而且我确信某个地方必须有一个最佳实践,表明在被处理之后不应该继续使用它!

但是,由于我无法对密封的SqlConnection进行子类化,并希望维护与事务无关的连接池友好方法,因此我很难(但会很高兴)看到更好的方法。

此外,意识到如果应用程序代码尝试打开嵌套连接(在大多数情况下,在我们的代码库中是不必要的),我可以通过抛出exception来强制非嵌套连接

 public static class DatabaseManager { private const string _connectionString = "data source=.\\sql2008; initial catalog=test; integrated security=true; enlist=true;Application Name='jimmy'"; [ThreadStatic] private static bool _transactionHooked; [ThreadStatic] private static bool _openConnection; public static SqlConnection GetOpenConnection() { var connection = new SqlConnection(_connectionString); if (Transaction.Current != null) { if (_openConnection) { throw new ApplicationException("Nested connections in transaction not allowed"); } _openConnection = true; connection.Disposed += ((s, e) => _openConnection = false); if (!_transactionHooked) { Transaction.Current.TransactionCompleted += ((s, e) => { _openConnection = false; _transactionHooked = false; }); _transactionHooked = true; } } connection.Open(); return connection; } } 

仍然会重视一个不太讨厌的解决方案:)

事务升级的主要原因之一是当事务中涉及多个(不同的)连接时。 这几乎总是升级为分布式事务。 这确实很痛苦。

这就是我们确保所有事务都使用单个连接对象的原因。 有几种方法可以做到这一点。 在大多数情况下,我们使用线程静态对象来存储连接对象,并且我们执行数据库持久性工作的类使用线程静态连接对象(当然是共享的)。 这可以防止使用多个连接对象,并消除了事务升级。 您也可以通过简单地将连接对象从方法传递给方法来实现这一点,但这不是一个干净的IMO。