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

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

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 ]

6 голосов
/ 31 мая 2011

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

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

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

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

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

0 голосов
/ 31 мая 2011

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

...