Почему вложенная транзакция фиксируется, даже если TransactionScope.Complete() никогда не вызывается?

Я проверял, как работают вложенные транзакции, и обнаружил это тревожное и неожиданное поведение.

using(TransactionScope otx = new TransactionScope())
using(SqlConnection conn1 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
using(SqlCommand cmd1 = conn1.CreateCommand())
{
    conn1.Open();
    cmd1.CommandType = CommandType.Text;
    cmd1.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (1,0x)";
    cmd1.ExecuteNonQuery();

    using(TransactionScope itx = new TransactionScope(TransactionScopeOption.RequiresNew))
    using(SqlConnection conn2 = new SqlConnection("Server=S;Database=DB;Trusted_Connection=yes"))
    using(SqlCommand cmd2 = conn1.CreateCommand())
    {
        conn2.Open();
        cmd2.CommandType = CommandType.Text;
        cmd2.CommandText = "INSERT INTO FP.ACLs (ChangeToken,ACL) VALUES (2,0x)";
        cmd2.ExecuteNonQuery();
        // we don't commit the inner transaction
    }

    otx.Complete(); // nonetheless, the inner transaction gets committed here and two rows appear in the database!
}

Я видел этот другой вопрос, но решение не применимо.

Если я не указываю TransactionScopeOption.RequiresNew (т.е. я не использую вложенную транзакцию, только вложенную область), тогда вся транзакция откатывается, когда внутренняя область не завершена, и возникает ошибка при вызове otx.Complete(). Это отлично.

Но я, конечно, не ожидаю, что вложенная транзакция будет завершена, если она не завершится успешно! Кто-нибудь знает, что здесь происходит и как я могу получить ожидаемое поведение?

База данных - SQL Server 2008 R2.

2 ответа

Во-первых, в SQL Server не существует такой вещи, как вложенная транзакция. Это важно.

Во-вторых, обе TransactionScopes используют conn1, поэтому вы (на уровне SQL Server) увеличиваете @@TRANCOUNT для каждого BEGIN TRANSACTION

Простое объяснение: внутренняя транзакция фиксируется, когда внешняя транзакция фиксируется, потому что откат внутренней будет откатывать обе транзакции

То есть, COMMIT TRANSACTION (подразумевается .Complete а также .Dispose) декременты @@TRANCOUNT в то время как ROLLBACK TRANSACTION (подразумевается .Dispose только) возвращает его к нулю. Таким образом, внутренний откат подавляется из-за того, что "нет такой вещи, как вложенные транзакции"

Если бы вы правильно использовали conn2 во внутренней области видимости, он бы работал как положено, потому что две транзакции не связаны на уровне сервера базы данных. Вот где это важно...

Ваш второй объект Command создается на conn1не conn2, так что это очень похоже на другой вопрос - соединение, по которому вы запускаете команду, было открыто до открытия второй области транзакции.

Другие вопросы по тегам