TransactionScope досрочно завершена

У меня есть блок кода, который выполняется в TransactionScope, и в этом блоке кода я делаю несколько вызовов БД. Выбирает, обновляет, создает и удаляет всю гамму. Когда я выполняю свое удаление, я выполняю его, используя метод расширения SqlCommand, который автоматически повторно отправит запрос, если он зайдет в тупик, поскольку этот запрос потенциально может зайти в тупик.

Я считаю, что проблема возникает, когда происходит взаимоблокировка, и функция пытается повторно отправить запрос. Я получаю вот такую ​​ошибку:

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

Это простой код, который выполняет запрос (весь приведенный ниже код выполняется с использованием TransactionScope):

using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App))
{
    sqlCommand.Connection.Open();
    sqlCommand.ExecuteNonQueryWithDeadlockHandling();
}

Вот метод расширения, который повторно отправляет заблокированный запрос:

public static class SqlCommandExtender
{
    private const int DEADLOCK_ERROR = 1205;
    private const int MAXIMUM_DEADLOCK_RETRIES = 5;
    private const int SLEEP_INCREMENT = 100;

    public static void ExecuteNonQueryWithDeadlockHandling(this SqlCommand sqlCommand)
    {
        int count = 0;
        SqlException deadlockException = null;

        do
        {
            if (count > 0) Thread.Sleep(count * SLEEP_INCREMENT);
            deadlockException = ExecuteNonQuery(sqlCommand);
            count++;
        }
        while (deadlockException != null && count < MAXIMUM_DEADLOCK_RETRIES);

        if (deadlockException != null) throw deadlockException;
    }

    private static SqlException ExecuteNonQuery(SqlCommand sqlCommand)
    {
        try
        {
            sqlCommand.ExecuteNonQuery();
        }
        catch (SqlException exception)
        {
            if (exception.Number == DEADLOCK_ERROR) return exception;
            throw;
        }

        return null;
    }
}

Ошибка возникает в строке:

sqlCommand.ExecuteNonQuery();

person Hungry Beast    schedule 27.05.2010    source источник


Ответы (7)


Не забудьте убрать операторы select из TransactionScope. В SQL Server 2005 и более поздних версиях, даже при использовании with (nolock), блокировки по-прежнему создаются для тех таблиц, которых касается выбранный элемент. Проверьте это, он показывает вам как настроить и используйте TransactionScope.

using(TransactionScope ts = new TransactionScope 
{ 
  // db calls here are in the transaction 
  using(TransactionScope tsSuppressed = new TransactionScope (TransactionScopeOption.Suppress)) 
  { 
    // all db calls here are now not in the transaction 
  } 
} 
person Dan    schedule 08.06.2010
comment
Спасибо за совет по подавлению транзакций в операторах select. Это помогло решить проблему с тайм-аутом, которая сводила меня с ума. - person RB Davidson; 16.08.2011
comment
Фантастический ответ. Это сводило меня с ума от коллекции инструкций sql для выбора / вставки. Добавление параметра «Подавить» автоматически решает проблему. - person Jacob; 24.02.2012
comment
Чёрт побери, спасибо! Я боролся с этим весь день. Такое простое решение. - person rossipedia; 05.10.2012
comment
Чрезмерная блокировка обычно является результатом установленного по умолчанию уровня изоляции Serializable TS. Обычно это лучше всего решается путем обертывания создания TS в classfactory, который устанавливает для сериализации нечто большее вменяемо, как прочитано, завершено - person StuartLC; 11.03.2013
comment
Я не понимаю. При чем здесь сообщение об ошибке? Связанная статья посвящена распределенным транзакциям. И зачем вам что-то подавлять - насколько вы блокируете, решает уровень изоляции. - person John; 28.06.2016
comment
Я согласен с @John, ответ на самом деле не решает проблему. - person Johan Boulé; 04.11.2016

Я обнаружил, что это сообщение может появиться, когда транзакция выполняется дольше, чем maxTimeout для System.Transactions. Не имеет значения, что TransactionOptions.Timeout увеличивается, оно не может превышать maxTimeout.

Значение по умолчанию maxTimeout составляет 10 минут, и его значение можно только изменить в machine.config

Добавьте следующее (на уровне конфигурации) в machine.config, чтобы изменить время ожидания:

<configuration>
    <system.transactions>
        <machineSettings maxTimeout="00:30:00" />
    </system.transactions>
</configuration>

Machine.config можно найти по адресу: %windir%\Microsoft.NET\Framework\[version]\config\machine.config

Вы можете узнать больше об этом в этом сообщении блога: http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html

person Marcus    schedule 04.04.2012
comment
Имейте в виду, что в файле конфигурации учитывается регистр, поэтому это должны быть machineSettings и maxTimeout. Жаль, что вы не можете переопределить это в файле app.config :( - person Bram Vandenbussche; 10.10.2012
comment
Также обратите внимание, что вы должны поместить это в конец раздела конфигурации, иначе вы получите сообщение об ошибке. - person Shaul Behr; 28.06.2015

Я могу воспроизвести проблему. Это тайм-аут транзакции.

using (new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 0, 1)))
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var sqlCommand = connection.CreateCommand())
        {
            for (int i = 0; i < 10000; i++)
            {
                sqlCommand.CommandText = "select * from actor";
                using (var sqlDataReader = sqlCommand.ExecuteReader())
                {
                    while (sqlDataReader.Read())
                    {
                    }
                }
            }
        }
    }
}

Выкидывает System.InvalidOperationException с таким сообщением:

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

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

person Rolf    schedule 17.02.2012

Если исключение происходит внутри TransactionScope, оно откатывается. Это означает, что TransactionScope готово. Теперь вы должны вызвать dispose() и начать новую транзакцию. Честно говоря, я не уверен, можно ли повторно использовать старый TransactionScope или нет, я никогда не пробовал, но полагаю, что нет.

person Donnie    schedule 27.05.2010
comment
Даже если исключение обнаружено, транзакция откатывается? - person Hungry Beast; 27.05.2010
comment
Я никогда не экспериментировал с этим, как по мне, исключение = ошибка = остановка и откат. Однако, судя по тому, что вы описываете, это так. - person Donnie; 27.05.2010
comment
Это неверно, и это не то, что происходит в исключениях. Тогда вам также не нужно вызывать Dispose (). Когда TransactionScope создается в операторе using, оператор using будет Dispose () TransactionScope on Exceptions. - person thewhiteambit; 26.03.2015
comment
При Dispose () TransactionScope либо откатится, либо зафиксируется, в зависимости от погоды, был вызван TransactionScope.Complete () или нет. Вот почему Complete () должен вызываться самым последним перед завершением блока using. Конечно, вы также можете вручную заблокировать Dispose () с помощью try-finally. Однако это не меняет ничего неправильного в поведении Dispose (), исключений и отката. Извините, пришлось проголосовать против. - person thewhiteambit; 26.03.2015
comment
У меня такая же проблема, но я могу понять ответ. Я даю вот так. используя (TransactionScope scope = new TransactionScope (TransactionScopeOption.RequiresNew)) - person Ziggler; 06.08.2015

Моя проблема была глупой, если вы сядете на отладку через тайм-аут, вы получите это. Лицо ладони

Блин, иногда программирование заставляет тебя чувствовать себя толстым ...

person Samuel Fleming    schedule 27.02.2012

Подтверждено, что эта ошибка также может быть вызвана тайм-аутом транзакции. Просто чтобы добавить к тому, что заявили Маркус + Рольф, если вы явно не установили тайм-аут для TransactionScope, тайм-аут TimeSpan примет значение по умолчанию. Это значение по умолчанию является меньшим из:

  1. Если вы переопределили локальную настройку app.config / web.config, например

    <system.transactions>
    <defaultSettings timeout="00:05:00" />
    </system.transactions>
    
  2. Но тогда это "ограничено" machine.config настройкой <machineSettings maxTimeout="00:10:00" />

person StuartLC    schedule 11.03.2013
comment
Могу ли я переопределить это в файле app.config? - person Kiquenet; 31.03.2017

Это исключение также может быть вызвано отключением Microsoft Distributed Transaction Coordinator.

Если мы хотим включить его, мы запускаем «dcomcnfg», выбираем "Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC" и выбираем «Свойства».

Необходимо установить флажки «Разрешить удаленный клиент», «Разрешить входящий», «Разрешить исходящий» и «Аутентификация не требуется ".

person Adrian Tarnowski    schedule 24.06.2015
comment
Можно ли включать / отключать программно с помощью BAT or ps1? - person Kiquenet; 14.05.2018