使用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检查代码),连接的设置(连接字符串等)将被重置,并且连接被关闭.所以(理论上),重新设置连接字符串并在后续调用中打开连接,应该“重用”连接并防止升级(我的初始测试与此同步).
它确实看起来有点黑客,但是我确信必须有一个最佳实践的地方说,一个人不应该继续使用一个对象后,被处置!
然而,由于我无法对密封的sqlConnection进行子类化,并且希望保持与事务无关的连接池友好方法,所以我努力(但会很高兴)看到更好的方法.
此外,意识到如果应用程序代码尝试打开嵌套连接(在大多数情况下是不必要的,在我们的代码库中),我可以通过抛出异常来强制非嵌套连接
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; } }
解决方法
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。