嵌套的TransactionScope和/或嵌套连接导致MSDTC升级

ena*_*ash 5 .net sql-server msdtc transactionscope escalation

我试图在我的应用程序中避免MSDTC升级.我正在使用LINQ和SQL Server Express 2008 R2,后来将使用完整版.

我编写了一个数据库包装器类,它根据需要创建连接并尽快处理它们.所有连接的连接字符串保持不变.

这是我班上非常精简的版本:

public class SqlServerDatabaseWrapper {

  public SqlServerDatabaseWrapper(string connectionString) {
    ConnectionString = connectionString; 
  }

  public string ConnectionString { get; private set; }

  private static IDbConnection GetOpenConnection() {
    var conn = new SqlConnection(ConnectionString);
    conn.Open();
    return conn;
  }

  // there is also a second method to return a value
  // there is PerformCommandAction for SqlCommand as well
  public void PerformDataContextAction<TContext>(Func<IDbConnection, TContext> creator, Action<TContext> action) where TContext : DataContext {
    PerformConnectionAction(conn => {
      using (var context = creator(conn))
        action(context);
    });
  }

  // there is also a second method to return a value
  public void PerformConnectionAction(Action<IDbConnection> action) {
    using (IDbConnection conn = GetOpenConnection(ConnectionString)) {
      action(conn);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

使用如下:

var db = new SqlServerDatabaseWrapper(connectionString);
db.PerformDataContextAction(
  conn => new SomeDataContext(conn), 
  context => { /* do something */ }
);
Run Code Online (Sandbox Code Playgroud)

如果我对PerformConnectionAction方法的内容进行了锁定,那么一次只能运行一个,那么一切正常,但是会有明显的性能损失.但是,当我删除它时,它会升级.

使用包装器的代码使用的是TransactionScope,可能存在TransactionScopes的嵌套和/或对PerformDataContextAction或PerformConnectionAction的调用(每个都创建一个具有相同连接字符串的新连接); 在伪代码中(因为这可能发生在不同的类/方法中):

var db = new SqlServerDatabaseWrapper(connectionString)
using (TransactionScope tran = new TransactionScope()) {
  db.PerformDataContextAction( 
    /* ... */,
    context => {
      using (TransactionScope tran2 = new TransactionScope()) {
        db.PerformConnectionAction(conn => { /* some stuff */ });
        tran2.Complete();
      }
    }
  tran.Complete();
}
Run Code Online (Sandbox Code Playgroud)

另请注意,可以在各个点使用静态成员资格方法.

我还要补充说连接字符串如下:

Data Source=.\SQLEXPRESS;Initial Catalog=db1;User Id=test1;Password=test1;MultipleActiveResultSets=true;Enlist=false;
Run Code Online (Sandbox Code Playgroud)

问题是,如何重构/重写我的代码,以便我的应用程序可以很好地执行,没有MSDTC,并且不会引入锁定?

谢谢

Bru*_*uno 1

您是否在事务范围内仅使用与数据库的一个连接?在事务范围内使用相同或不同的连接字符串创建两个连接会将事务升级为分布式事务。

您可以将连接存储在线程静态变量中,并在事务中的所有工作完成后关闭/处置它。然后每个线程都会有它自己的连接。

当您向逻辑添加锁时,您可能不会获得分布式事务,因为连接池每次都会返回相同的连接。