Что означает, что SqlConnection «зачислен» в транзакцию? Означает ли это просто, что команды, которые я выполняю для соединения, будут участвовать в транзакции?
Если да, то при каких обстоятельствах SqlConnection автоматически зачисляется в внешнюю транзакцию TransactionScope?
Смотрите вопросы в комментариях к коду. Мое предположение на ответ на каждый вопрос следует за каждым вопросом в скобках.
Сценарий 1. Открытие соединения ВНУТРИ области действия транзакции
using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = ConnectToDB())
{
// Q1: Is connection automatically enlisted in transaction? (Yes?)
//
// Q2: If I open (and run commands on) a second connection now,
// with an identical connection string,
// what, if any, is the relationship of this second connection to the first?
//
// Q3: Will this second connection's automatic enlistment
// in the current transaction scope cause the transaction to be
// escalated to a distributed transaction? (Yes?)
}
Сценарий 2. Использование соединений ВНУТРИ области транзакции, которая была открыта ВНЕ
//Assume no ambient transaction active now
SqlConnection new_or_existing_connection = ConnectToDB(); //or passed in as method parameter
using (TransactionScope scope = new TransactionScope())
{
// Connection was opened before transaction scope was created
// Q4: If I start executing commands on the connection now,
// will it automatically become enlisted in the current transaction scope? (No?)
//
// Q5: If not enlisted, will commands I execute on the connection now
// participate in the ambient transaction? (No?)
//
// Q6: If commands on this connection are
// not participating in the current transaction, will they be committed
// even if rollback the current transaction scope? (Yes?)
//
// If my thoughts are correct, all of the above is disturbing,
// because it would look like I'm executing commands
// in a transaction scope, when in fact I'm not at all,
// until I do the following...
//
// Now enlisting existing connection in current transaction
conn.EnlistTransaction( Transaction.Current );
//
// Q7: Does the above method explicitly enlist the pre-existing connection
// in the current ambient transaction, so that commands I
// execute on the connection now participate in the
// ambient transaction? (Yes?)
//
// Q8: If the existing connection was already enlisted in a transaction
// when I called the above method, what would happen? Might an error be thrown? (Probably?)
//
// Q9: If the existing connection was already enlisted in a transaction
// and I did NOT call the above method to enlist it, would any commands
// I execute on it participate in it's existing transaction rather than
// the current transaction scope. (Yes?)
}
Отличная работа, Трайнко, все ваши ответы выглядят довольно точно и полно для меня. Некоторые другие вещи, на которые я хотел бы обратить внимание:
(1) Ручное зачисление
В приведенном выше коде вы (правильно) показываете ручное зачисление следующим образом:
Тем не менее, это также возможно сделать так, используя Enlist = false в строке подключения.
Здесь есть еще одна вещь, которую стоит отметить. Когда conn2 открыт, код пула соединений не знает, что вы хотите позже подключить его к той же транзакции, что и conn1, что означает, что conn2 получает другое внутреннее соединение, чем conn1. Затем, когда зачислен conn2, теперь зачислено 2 соединения, поэтому транзакция должна быть переведена в MSDTC. Этой акции можно избежать только с помощью автоматической регистрации.
(2) До .Net 4.0 я настоятельно рекомендовал установить «Привязка транзакции = Явная отмена привязки» в строке подключения . Эта проблема исправлена в .Net 4.0, что делает явную отмену привязки совершенно ненужной.
(3) Откатить свое
CommittableTransaction
и установитьTransaction.Current
это по сути то же самое, что и то, чтоTransactionScope
делает. Это редко на самом деле полезно, просто к вашему сведению.(4)
Transaction.Current
является потоково-статическим. Это означает, чтоTransaction.Current
это установлено только в потоке, который создалTransactionScope
. Поэтому несколько потоков, выполняющих одно и то жеTransactionScope
(возможно, использующихTask
), невозможно.источник
Еще одна странная ситуация, с которой мы столкнулись, заключается в том, что, если вы создадите ее,
EntityConnectionStringBuilder
она будет портитьTransactionScope.Current
и (мы думаем) подключиться к транзакции. Мы наблюдали это в отладчике, гдеTransactionScope.Current
«Scurrent.TransactionInformation.internalTransaction
показывает ,enlistmentCount == 1
прежде чем строить, аenlistmentCount == 2
потом.Чтобы избежать этого, постройте его внутри
using (new TransactionScope(TransactionScopeOption.Suppress))
и, возможно, выходит за рамки вашей операции (мы создавали ее каждый раз, когда нам требовалось подключение).
источник