При каких обстоятельствах SqlConnection автоматически включается в список в окружающую Транзакцию TransactionScope?

Что означает то, чтобы 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: Используя соединения В области транзакций, которые были открыты OUTSIDE of это

//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?)
}
198
задан Triynko 7 December 2010 в 11:59
поделиться

1 ответ

Я провел несколько тестов после того, как задал этот вопрос, и нашел большинство, если не все ответы, самостоятельно, поскольку никто больше не ответил. Пожалуйста, дайте мне знать, если я что-то упустил.

Q1. Да, если в строке подключения не указано "enlist=false". Пул соединений находит пригодное для использования соединение. Полезное соединение - это то, которое не зачислено в транзакцию или то, которое зачислено в ту же транзакцию.

Q2. Второе соединение - это независимое соединение, которое участвует в той же транзакции. Я не уверен насчет взаимодействия команд на этих двух соединениях, поскольку они работают с одной и той же базой данных, но я думаю, что могут возникнуть ошибки, если команды выдаются на обоих одновременно: ошибки типа "Transaction context in use by another session"

Q3. Да, она переходит в разряд распределенных транзакций, поэтому зачисление более одного соединения, даже с одной и той же строкой подключения, приводит к тому, что она становится распределенной транзакцией, что можно подтвердить, проверив наличие ненулевого GUID в Transaction.Current.TransactionInformation.DistributedIdentifier. *Обновление: я где-то читал, что это исправлено в SQL Server 2008, так что MSDTC не используется, когда одна и та же строка соединения используется для обоих соединений (пока оба соединения не открыты одновременно). Это позволяет вам открывать и закрывать соединение несколько раз в рамках одной транзакции, что может лучше использовать пул соединений, открывая соединения как можно позже и закрывая их как можно быстрее.

Q4. Нет. Соединение, открытое, когда ни одна область транзакции не была активна, не будет автоматически включено во вновь созданную область транзакции.

Q5. Нет. Пока вы не откроете соединение в области транзакций или не включите существующее соединение в область транзакций, никакой транзакции не будет. Ваше соединение должно быть автоматически или вручную включено в область действия транзакции, чтобы ваши команды могли участвовать в транзакции.

Q6. Да, команды на соединении, не участвующем в транзакции, фиксируются как выданные, даже если код выполнялся в блоке области действия транзакции, который был откачен. Если соединение не включено в текущую область видимости транзакции, оно не участвует в транзакции, поэтому фиксация или откат транзакции никак не повлияют на команды, выданные на соединение, не включенное в область видимости транзакции... как выяснил этот парень. Это очень трудно заметить, если вы не понимаете процесс автоматического зачисления: он происходит только тогда, когда соединение открывается внутри активной области действия транзакции.

Q7. Да. Существующее соединение можно явно зачислить в текущую область действия транзакции, вызвав EnlistTransaction( Transaction.Current ). Вы также можете зачислить соединение в отдельном потоке транзакции, используя DependentTransaction, но, как и раньше, я не уверен, как могут взаимодействовать два соединения, участвующие в одной транзакции с одной и той же базой данных... могут возникнуть ошибки, и, конечно, второе зачисленное соединение приводит к перерастанию транзакции в распределенную транзакцию.

Q8. Может возникнуть ошибка. Если использовался TransactionScopeOption.Required, и соединение уже было зачислено в транзакцию диапазона транзакций, то ошибки не возникает; фактически, для диапазона не создается новая транзакция, и счетчик транзакций (@@trancount) не увеличивается. Однако если вы используете TransactionScopeOption.RequiresNew, то при попытке зачислить соединение в новую транзакцию диапазона транзакций вы получите полезное сообщение об ошибке: "Соединение в настоящее время занесено в список транзакций. Завершите текущую транзакцию и повторите попытку". И да, если вы завершите транзакцию, в которую включено соединение, вы сможете безопасно включить соединение в новую транзакцию. Обновление: Если вы ранее вызывали BeginTransaction для соединения, то при попытке зачислить соединение в новую транзакцию возникает немного другая ошибка: "Невозможно записаться в транзакцию, поскольку на соединении выполняется локальная транзакция. Завершите локальную транзакцию и повторите попытку". С другой стороны, вы можете безопасно вызвать BeginTransaction на SqlConnection, пока оно включено в транзакционную область, и это действительно увеличит @@trancount на единицу, в отличие от использования опции Required вложенной области транзакции, которая не приводит к увеличению. Интересно, что если затем создать еще одну вложенную область видимости транзакции с параметром Required, то ошибка не возникнет, потому что ничего не меняется в результате того, что транзакция уже активна (помните, что @@trancount не увеличивается, когда транзакция в рамках транзакции уже активна и используется опция Required).

Q9. Да. Команды участвуют в любой транзакции, в которую включено соединение, независимо от того, какова активная область действия транзакции в коде C#.

183
ответ дан 23 November 2019 в 05:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: