Я ищу описание корня этой ошибки: "Контекст транзакции, используемый другой сессией".
Я иногда получаю его в одном из моих unittests, таким образом, я не могу размноженный код поставщика. Но интересно, что "дизайном" причина ошибки.
ОБНОВЛЕНИЕ: ошибка возвращается как SqlException из SQL Server 2008. Место, где я получаю ошибку, кажется, является однопоточным. Но вероятно у меня есть unittests взаимодействие, поскольку я получаю ошибку, где запущено несколько тестов сразу (MSTest в VS2008sp1). Но провальный тест похож:
.
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()
Я нашел эти сообщения:
Но я не могу понять то, что "Несколько потоков, совместно использующих ту же транзакцию в области транзакций, вызовут следующее исключение: 'Контекст транзакции, используемый другой сессией'". средства. Все слова понятны, но не точка.
Я на самом деле могу совместно использовать системную транзакцию между потоками. И существует даже специальный механизм для этого - класс DependentTransaction и Транзакция. Метод 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);
}
}
Это перестало работать на вызове Complete корневого TransactionScope. Но ошибка отличается: Необработанное исключение: Система. Транзакции. TransactionInDoubtException: транзакция вызывает сомнение.---> вырванный. Период тайм-аута протек до завершения операции, или сервер не отвечает.
Подводя итоги: Я хочу понять то, что "Контекст транзакции, используемый другой сессией" средства и как воспроизвести его.
Сделайте шаг назад и сосредоточьтесь больше на своем коде, а не на информации о нескольких потоках, плавающей вокруг.
Если ваш сценарий не включает многопоточность, он может относиться к частям, которые не закрыты так, как вы ожидаете.
Возможно, вызываемый вами sql-код не достигает этой инструкции транзакции фиксации. Или на этом уровне задействовано что-то еще. Возможно, вы использовали экземпляр SqlConnection, устанавливающий транзакцию в коде .net, и повторно используете тот же экземпляр в другом коде, который использует TransactionScope. Попробуйте добавить инструкции using (), где это необходимо, чтобы убедиться, что все закрыто так, как вы ожидаете.
"Несколько потоков используют один и тот же транзакция в области транзакции вызовет следующее исключение: 'Контекст транзакции используется другим session. '"
Звучит довольно просто. Если вы подключите два разных соединения к одной транзакции, а затем попытаетесь выполнить команды для каждого из двух соединений одновременно из разных потоков, может возникнуть конфликт.
В другом случае Другими словами, один поток выдает команду в одном соединении и удерживает некоторую блокировку контекста транзакции. Другой поток, используя другое соединение, пытается выполнять команды одновременно и не может заблокировать тот же контекст транзакции, что является используется другим потоком.
Как я решаю эту проблему при построении Linq-запросов с несколькими объектами, я бы сделал конструктор для каждого класса, который принимает контекст данных, и соответствующий метод GetDataContext() в каждом классе. При объединении классов я бы создавал новые экземпляры классов, передавая GetContext() первого класса
public class CriterionRepository : ICriterionRepository
{
private Survey.Core.Repository.SqlDataContext _context = new Survey.Core.Repository.SqlDataContext();
public CriterionRepository() { }
public CriterionRepository(Survey.Core.Repository.SqlDataContext context)
{
_context = context;
}
...
public Survey.Core.Repository.SqlDataContext GetDataContext()
{
return _context;
}
}