嵌套的TransactionScope和/或嵌套连接引起了MSDTC升级问题

5

我正在尝试避免应用程序中的MSDTC升级。 我正在使用带有SQL Server Express 2008 R2的LINQ,稍后将使用完整版本。

我编写了一个数据库包装器类,根据需要创建连接并尽快处理它们。 所有连接都使用相同的连接字符串。

这是我的类的精简版本:

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);
    }
  }
}

使用方法如下:

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

如果我在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();
}

请注意,代码中使用了静态 Membership 方法,这可能会在不同的地方出现。
此外,连接字符串如下:
Data Source=.\SQLEXPRESS;Initial Catalog=db1;User Id=test1;Password=test1;MultipleActiveResultSets=true;Enlist=false;

问题是,我该如何重构/重写我的代码,使我的应用程序在没有MSDTC和不引入锁的情况下能够良好运行?感谢。

你正在使用哪个版本的SQL Server? - Oded
SQL Express 2008 R2,稍后将升级到完整版。 - enashnash
我有点困惑,为什么添加锁可以防止升级,因为TransactionScope是线程绑定的,而锁只影响线程之间的交互,但不影响线程内操作的顺序,除非您的线程引入了非确定性行为,在这种情况下,我可以看到锁会改变事情。如果您仍然对答案感兴趣,请让我们更多地了解线程方面的情况。 - Evgeniy Berezovsky
1个回答

1

您在事务范围内只使用了一个数据库连接吗? 在事务范围内创建两个具有相同或不同连接字符串的连接将升级事务为分布式事务。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接