Если SPID разные то и сессии разные.
Сессии разные - значит, коннекшены тоже разные, сессия одна - коннекшен один, так же?
Мне передали прогу на EntityFramework, внутри нее подряд несколько раз создается new DbContext. Каждый DbContext обернут в свою собственую TransactionScope. Т.к. все DbContext представляют одну логич. операцию, то сейчас реализовано коряво, и мне нужно убрать все индивидуальные транзакции у каждого DbContext и обернуть их всех в 1 транзакцию. Чтобы много не переделывать, я пока решил не заменять создание нескольких DbContext на 1, а просто обернул все DbContext внутрь одного TransactionScope. У первого DbContext я удалил его собственную транзакцию, у второго забыл удалить (получилось, что у меня в проге стало 2 транзакции - внешняя и вложенная). Получился сокращенно такой код:
using (var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions {IsolationLevel = IsolationLevel.Snapshot}))
{
var data1Id;
using (var dbContext = new DB())
{
var data1= new Data1();
dbContext.Table1.Add(data1);
/*1*/dbContext.SaveChanges();
data1Id = data1.Id;
}
if (data1Id != 0)
{
using (var transactionScope2 = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions {IsolationLevel = IsolationLevel.Snapshot}))
{
using (var dbContext = new DB())
{
dbContext.Table2.Add(new Data2() { Data1Id = data1Id, ... });
/*2*/dbContext.SaveChanges();
transactionScope2.Complete();
}
}
}
transactionScope.Complete();
}
Запустил прогу, первый DbContext выполнил запросы внутри внешней транзакции (произошел инсерт, но пока без коммита), второй DbContext начал выполнять запросы внутри своей вложенной транзакции. В итоге второй DbContext простоял в дебаге полминуты, а потом кинулся эксепшен что-то типа "ожидание времени окончания операции истекло", все транзакции роллбэкнулись. Я посмотрел в SQL Server Profiler - запросы из разных DbContext имели разный SPID. В итоге я сделал себе такой вывод - т.к. SPID разные, то и коннекшены были разные, значит, sql сервер воспринимал запросы от разных DbContext как параллельные (каждый в своих собственных транзакциях), хотя по логике проги это должны были быть взаимозависимые последовательные запросы внутри одной транзакции. В итоге произошла взаимная блокировка (кстати, я не понял, почему, т.к. разные DbContext инсертили в разные таблицы), и транзакция откатилась (или какая там ошибка могла произойти?).
Получается, что транзакция откатилась, потому что 2 разных DbContext имели разные транзакции и разные сессии (коннекшены). Если бы был 1 общий DbContext (соотвественно, 1 общий коннекшен), то транзакция бы выполнилась успешно, как я понимаю.
Я начал гуглить и нашел те ссылки. Если я их правильно понял, в них говорится, что не факт, что 2 разных DbContext будут иметь разные коннекшены, потому что "они шарят общую строку подключения, и поэтому коннекшен для второго DbContext может взяться тот же самый, что и для первого DbContext, потому что коннекшен первого DbContext сохранится в пуле коннекшенов даже после закрытия первого DbContext". Вот это меня и напрягло и заставило тут задать вопрос. Потому что это же получается, что если у меня многопользовательская система и разные контроллеры обрабатывают независимые запросы разных юзеров в разных потоках, и каждый контроллер, естественно, создает свой new DbContext , то даже если я оберну весь код в транзакции, то все-равно строка подключения к БД одинаковая у всех DbContext, значит, они могут юзать один-единственный коннекшен из пула коннекшенов, это значит, что PSID будет один и тот же, и sql сервер будет выполнять их в общей транзакции, а не в индивидуальных. Но такого же не может быть, иначе бы просто не было смысла открывать транзакции.
Кто-нибудь может это объяснить?
Еще то, что я не понял:
1) в коде выше после прохождения точки
/*1*/dbContext.SaveChanges(); профайлер пишет "BEGIN TRANSACTION" и PSID = 53 (все вроде бы правильно, транзакция началась во время первого инсерта), а в точке
/*2*/dbContext.SaveChanges(); профайлер пишет "BEGIN TRANSACTION" и PSID = 59 (но тут я не понял, транзакция второго DbContext же вложенная, но в новой сессии с PSID = 59 открывается только один "BEGIN TRANSACTION". Это значит, что первая (внешняя) транзакция теряется для всех последующих открытых сессий после того, как первая сессия "захватила" во время выполнения инсерта эту транзакцию, несмотря на то, что все последующие сессии в коде открываются внутри этой внешней транзакции? Или как? Это значит, что вторая сессия выполнялась только внутри одной транзакции, хотя в коде она вложена в 2 транзакции?).
2) уровень изоляции задан как snaphot, но это никак не отображается в профайлере. Профайлер показывает только строку "BEGIN TRANSACTION". Я хочу убедиться, что на sql сервер правильно передается уровень изоляции, но не могу, т.к. не вижу этого в профайлере. Возможно, нужно включить больше чекбоксов во вкладке "выбор событий", но я не могу найти по названию событий, какой чекбокс нужно для этого включить. В общем, подскажите, что сделать, чтобы профайлер показывал уровень изоляции.