ADO.NET ошибка транзакции зомби? Как обеспечить, чтобы команды не выполнялись при неявной транзакции? - PullRequest
2 голосов
/ 07 июня 2010

например. При возникновении взаимоблокировки следующие команды SQL успешно выполняются, даже если им назначена транзакция SQL после отката. Кажется, это вызвано новой неявной транзакцией, созданной на SQL Server.

Кто-то может ожидать, что ADO.NET выдаст исключение, что команды выполняются в транзакции зомби. Однако такое исключение не выбрасывается. (Я думаю, что это ошибка в ASP.NET.) Более того, из-за транзакции зомби финальная Dispose() молча игнорирует откат.

Есть идеи, как я могу гарантировать, что никто не сможет выполнять команды при неявной транзакции? Или как проверить, что транзакция зомби? Я обнаружил, что Commit() и Rollback() проверяют транзакцию зомби, однако я могу вызвать их для проверки:)

Я также обнаружил, что проверка также выполняется при чтении IsolationLevel, но я не уверен, что будущий оптимизатор не удалит простой вызов transaction.IsolationLevel.ToString();. Или вы знаете какой-либо другой безопасный способ вызвать геттер (без использования отражения или IL-излучения)?

РЕДАКТИРОВАТЬ: Ремус Русану указал, что эта ситуация обычно не происходит. Да, это правда. Обычно это происходит, когда в коде есть ошибка. В нашем случае в операторе finally была некоторая подпрограмма регистрации, которая пыталась сохранить ошибку в базе данных. Сейчас я пытаюсь найти решение, как обнаружить такие ошибки в будущем. Так как эти ошибки сложно проверить. Если ADO.NET проверит, что предоставленная транзакция - зомби, эта ошибка будет обнаружена гораздо проще. Я нашел две возможности:

  1. Отключить создание неявных транзакций - я не уверен, возможно ли это.
  2. Убедитесь, что перед выполнением любых команд будет запущена проверка транзакции зомби.

Ответы [ 3 ]

2 голосов
/ 07 июня 2010

То, что вы описываете, не существует. Откат транзакции вызовет очень заметное исключение в вашем приложении. по некоторым причинам я скорее верю, что ваш код перехватывает исключение и молча игнорирует его, продолжая выполнять операторы после отката транзакции.

0 голосов
/ 21 июня 2010

// Исходя из вашего описания, я предполагаю, что ваш код эффективно делает это

        SqlConnection conn = new SqlConnection("ConnectionString");
        SqlCommand cmd = new SqlCommand("insert into ....");

        cmd.Connection = conn;

        conn.Open();

        SqlTransaction tran = conn.BeginTransaction();


        cmd.Transaction = tran;

        tran.Rollback(); //or tran.Dispose();

        cmd.ExecuteNonQuery();

Это приводит к выполнению cmd вне области транзакции.

Удаление строки cmd.Connection = conn; добьется того поведения, которое, я думаю, вы ищете (например, команда не выполнится, поскольку транзакция больше не действительна)

SqlConnection conn = new SqlConnection("ConnectionString");
    SqlCommand cmd = new SqlCommand("insert into ....");

    //cmd.Connection = conn;

    conn.Open();

    SqlTransaction tran = conn.BeginTransaction();
    cmd.Connection = tran.Connection;

    cmd.Transaction = tran;

    tran.Rollback(); //or tran.Dispose();

    cmd.ExecuteNonQuery();
0 голосов
/ 20 июня 2010

Возможно, это не имеет прямого отношения к вашей проблеме, так как вызвано ошибкой, но все же может представлять интерес.Не все ошибки приводят к откату транзакции, поэтому иногда транзакция может быть «частично успешной» - некоторые операторы допустили ошибку, в то время как другие конкурировали нормально.
Существует опция SET XACT_ABORT ON , которая заставляет сервер прервать транзакцию.при любой ошибке.

Учитывая ваш вопрос, вы не можете отключить неявные транзакции (если вы выполняете оператор SQL, неявная транзакция будет создана, если другая транзакция уже активна).Поэтому вам просто нужно корректно обработать ошибки, чтобы убедиться, что транзакция существует, когда она вам нужна.
Посмотрите на класс TransactionScope , вы можете использовать его, чтобы избежать управления этими транзакциями в своем коде.

...