Shr*_*ike 14 .net sql-server transactions transactionscope
我正在寻找这个错误根源的描述:"另一个会话正在使用的事务上下文".
我有时会在我的一个单元测试中得到它,所以我不能提供repro代码.但我想知道什么是"按设计"错误的原因.
更新:错误从SQL Server 2008返回为SqlException.我收到错误的地方似乎是单线程的.但是我可能有单元测试交互,因为我得到了错误,一次运行几个测试(VS2008sp1中的MSTest).但失败的测试看起来像:
.
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()
Run Code Online (Sandbox Code Playgroud)
我找到了这些帖子:
但我无法理解"在事务范围内共享同一事务的多个线程将导致以下异常:'另一个会话正在使用的事务上下文'."表示.所有的话都是可以理解的,但不是重点.
我实际上可以在线程之间共享系统事务.甚至还有一种特殊的机制--DependentTransaction类和Transaction.DependentClone方法.
我试图从第一篇文章中重现一个用例:
用这样的代码:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
它在完成对根TransactionScope的调用时失败.但错误是不同的:未处理的异常:System.Transactions.TransactionInDoubtException:该事务存在疑问.--->很高兴.操作完成之前经过的超时时间或服务器没有响应.
总结一下:我想了解"另一个会话使用的事务上下文"是什么意思以及如何重现它.
小智 5
回答:)有点晚了,但希望对其他人有用。答案包含三个部分:
1.什么意思是“另一个会话正在使用事务上下文”。
重要说明:事务上下文锁是在SqlConnection与SQL Server 之间进行交互之前获取的,并在与SQL Server 之间进行交互后立即释放。
当您执行某些SQL查询时,SqlConnection“查找”会包含任何包装它的事务。它可能是SqlTransaction(对于SqlConnection为“本地”)或Transaction来自System.Transactions程序集。
找到事务后,便SqlConnection使用它与SQL Server进行通信,此刻,它们之间的通信Transaction上下文被完全锁定。
这是什么TransactionScope?它创建Transaction并提供有关它的.NET Framework组件信息,因此包括SqlConnection在内的每个人都可以(并且在设计上应使用)它。
因此,声明TransactionScope我们正在创建新的Transaction,该事务可用于在current中实例化的所有“可交易”对象Thread。
描述的错误表示以下内容:
SqlConnections相同的条件下创建了多个TransactionContext(这意味着它们与同一笔交易相关)SqlConnection同时与SQL Server通信Transaction上下文,下一个引发了错误2.如何重现错误“另一个会话正在使用的事务上下文”。
首先,在执行sql命令时立即使用(“锁定”)事务上下文。因此,很难肯定地重现这种行为。
但是我们可以尝试通过在单个事务下启动多个运行相对较长的SQL操作的线程来做到这一点。让我们准备表[dbo].[Persons]在[tests]数据库:
USE [tests]
GO
DROP TABLE [dbo].[Persons]
GO
CREATE TABLE [dbo].[Persons](
[Id] [bigint] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[Name] [nvarchar](1024) NOT NULL,
[Nick] [nvarchar](1024) NOT NULL,
[Email] [nvarchar](1024) NOT NULL)
GO
DECLARE @Counter INT
SET @Counter = 500
WHILE (@Counter > 0) BEGIN
INSERT [dbo].[Persons] ([Name], [Nick], [Email])
VALUES ('Sheev Palpatine', 'DarthSidious', 'spalpatine@galaxyempire.gov')
SET @Counter = @Counter - 1
END
GO
Run Code Online (Sandbox Code Playgroud)
并重现“另一个会话正在使用的事务上下文”。基于Shrike代码示例的C#代码错误
using System;
using System.Collections.Generic;
using System.Threading;
using System.Transactions;
using System.Data.SqlClient;
namespace SO.SQL.Transactions
{
public static class TxContextInUseRepro
{
const int Iterations = 100;
const int ThreadCount = 10;
const int MaxThreadSleep = 50;
const string ConnectionString = "Initial Catalog=tests;Data Source=.;" +
"User ID=testUser;PWD=Qwerty12;";
static readonly Random Rnd = new Random();
public static void Main()
{
var txOptions = new TransactionOptions();
txOptions.IsolationLevel = IsolationLevel.ReadCommitted;
using (var ctx = new TransactionScope(
TransactionScopeOption.Required, txOptions))
{
var current = Transaction.Current;
DependentTransaction dtx = current.DependentClone(
DependentCloneOption.BlockCommitUntilComplete);
for (int i = 0; i < Iterations; i++)
{
// make the transaction distributed
using (SqlConnection con1 = new SqlConnection(ConnectionString))
using (SqlConnection con2 = new SqlConnection(ConnectionString))
{
con1.Open();
con2.Open();
}
var threads = new List<Thread>();
for (int j = 0; j < ThreadCount; j++)
{
Thread t1 = new Thread(o => WorkCallback(dtx));
threads.Add(t1);
t1.Start();
}
for (int j = 0; j < ThreadCount; j++)
threads[j].Join();
}
dtx.Complete();
ctx.Complete();
}
}
private static void WorkCallback(DependentTransaction dtx)
{
using (var txScope1 = new TransactionScope(dtx))
{
using (SqlConnection con2 = new SqlConnection(ConnectionString))
{
Thread.Sleep(Rnd.Next(MaxThreadSleep));
con2.Open();
using (var cmd = new SqlCommand("SELECT * FROM [dbo].[Persons]", con2))
using (cmd.ExecuteReader()) { } // simply recieve data
}
txScope1.Complete();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后,关于在您的应用程序中实现事务支持的几句话:
SELECT/ UPDATE/ etc ...请求保存在单个队列中,并使用单线程工作程序处理这些请求;TransactionScope。默认值是,Serializable但是在大多数情况下ReadCommitted足够了;TransactionScope和DependentTransaction“多个线程在事务范围内共享同一事务将导致以下异常:‘事务上下文正在被另一个会话使用。’”
听起来很简单。如果您在同一事务中登记两个不同的连接,然后尝试从不同的线程同时对这两个连接发出命令,则可能会发生冲突。
换句话说,一个线程在一个连接上发出命令,并在事务上下文上持有某种锁。使用另一个连接的另一个线程尝试同时执行命令,并且无法锁定另一个线程正在使用的同一事务上下文。
| 归档时间: |
|
| 查看次数: |
22606 次 |
| 最近记录: |