【发布时间】:2010-05-18 15:36:54
【问题描述】:
我正在寻找此错误根源的描述:“另一个会话正在使用事务上下文”。
我有时会在我的一个单元测试中得到它,所以我无法提供重现代码。但我想知道错误的“设计”原因是什么。
更新:错误从 SQL Server 2008 返回为 SqlException。我得到错误的地方似乎是单线程的。但可能我有单元测试交互,因为我得到一次运行多个测试的错误(VS2008sp1 中的 MSTest)。 但是失败的测试看起来像:
- 创建一个对象并将其保存在 DB-transaction(提交)中
- 创建 TransactionScope
- 试图打开一个连接 - 在这里我得到了带有这样的堆栈跟踪的 SqlException:
.
System.Data.SqlClient.SqlException: Transaction context in use by another session.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
at System.Data.SqlClient.SqlInternalConnectionTds.PropagateTransactionCookie(Byte[] cookie)
at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlConnection.Open()
我找到了这些帖子:
- http://blogs.msdn.com/asiatech/archive/2009/08/10/system-transaction-may-fail-in-multiple-thread-environment.aspx
- http://msdn.microsoft.com/en-us/library/ff649002.aspx
但我不明白“在事务范围内共享同一事务的多个线程会导致以下异常:'Transaction context in use by another session.'” 是什么意思。所有的话都可以理解,但不是重点。
我实际上可以在线程之间共享系统事务。甚至还有一种特殊的机制——DependentTransaction 类和 Transaction.DependentClone 方法。
我正在尝试从第一篇文章中重现一个用例:
- 主线程创建DTC事务,接收DependentTransaction(在主线程上使用Transaction.Current.DependentClone创建
- 子线程 1 通过基于依赖事务(通过构造函数传递)创建事务范围来加入此 DTC 事务
- 子线程 1 打开一个连接
- 子线程 2 通过基于依赖事务(通过构造函数传递)创建事务范围来加入 DTC 事务
- 子线程 2 打开一个连接
使用这样的代码:
using System;
using System.Threading;
using System.Transactions;
using System.Data;
using System.Data.SqlClient;
public class Program
{
private static string ConnectionString = "Initial Catalog=DB;Data Source=.;User ID=user;PWD=pwd;";
public static void Main()
{
int MAX = 100;
for(int i =0; i< MAX;i++)
{
using(var ctx = new TransactionScope())
{
var tx = Transaction.Current;
// make the transaction distributed
using (SqlConnection con1 = new SqlConnection(ConnectionString))
using (SqlConnection con2 = new SqlConnection(ConnectionString))
{
con1.Open();
con2.Open();
}
showSysTranStatus();
DependentTransaction dtx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
Thread t1 = new Thread(o => workCallback(dtx));
Thread t2 = new Thread(o => workCallback(dtx));
t1.Start();
t2.Start();
t1.Join();
t2.Join();
ctx.Complete();
}
trace("root transaction completes");
}
}
private static void workCallback(DependentTransaction dtx)
{
using(var txScope1 = new TransactionScope(dtx))
{
using (SqlConnection con2 = new SqlConnection(ConnectionString))
{
con2.Open();
trace("connection opened");
showDbTranStatus(con2);
}
txScope1.Complete();
}
trace("dependant tran completes");
}
private static void trace(string msg)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " : " + msg);
}
private static void showSysTranStatus()
{
string msg;
if (Transaction.Current != null)
msg = Transaction.Current.TransactionInformation.DistributedIdentifier.ToString();
else
msg = "no sys tran";
trace( msg );
}
private static void showDbTranStatus(SqlConnection con)
{
var cmd = con.CreateCommand();
cmd.CommandText = "SELECT 1";
var c = cmd.ExecuteScalar();
trace("@@TRANCOUNT = " + c);
}
}
它在 Complete 调用根 TransactionScope 时失败。但错误是不同的: 未处理的异常:System.Transactions.TransactionInDoubtException:事务有问题。 ---> 已过期。在操作完成之前超时时间已过或服务器没有响应。
总结一下:我想了解“另一个会话正在使用的事务上下文”是什么意思以及如何重现它。
【问题讨论】:
-
一个小问题:你确定你有分布式事务吗?你建立了 2 个开放的连接: con1.Open(); con2.Open();但是连接字符串是相同的,并且您使用的是 Sql2008。 AFAIK 如果您使用 Sql2008 和相同的连接字符串,则事务不会升级为分布式。它仍然是“本地的”。我的 2 美分。
-
不,同时打开两个连接总是会导致分布式事务。它们是否具有相同的连接字符串并不重要
-
@Fabrizio,但如果同一个连接实例连续打开和关闭,那么在 sql2008 中我们得到本地 tran 但在 sql2005 中是分布式 tran。
标签: .net sql-server transactions transactionscope