Максимальный тайм-аут TransactionScope

Я использую TransactionScope в этом коде:

private void ExecuteSP()
{
    bool IsComplete = false;
    SqlCommand sqlComm = null;
    //6 hours!!!
    TimeSpan ts1 = new TimeSpan(6, 0, 0);
    try
    {
        using (TransactionScope t = new TransactionScope(TransactionScopeOption.RequiresNew, ts1))
        {
            using (SqlConnection sqlConn = new SqlConnection(GetConnectionString()))
            {
                //open sql connection
                sqlConn.Open();
                try
                {
                    //create new sqlCommand
                    sqlComm = new SqlCommand();
                    for (int i = 1; i <= 2; i++)
                    {
                        IsComplete = true;
                        //This command takes 15 minutes
                        sqlComm.CommandText = "exec TestSp";
                        sqlComm.Connection = sqlConn;
                        sqlComm.CommandType = CommandType.Text;
                        sqlComm.CommandTimeout = 18000;
                        //Executing my command
                        int j = sqlComm.ExecuteNonQuery();                       
                    }
                    //End
                    t.Complete();
                }
                catch (Exception ex)
                {
                    IsComplete = false;
                    string Message = ex.Message;
                }
                finally
                {
                    if (sqlComm != null)
                        sqlComm.Dispose();                 
                }
            }
        }
    }
    catch (Exception ex)
    {
        string messagee = ex.Message;
        //do something
    }
    finally
    {
        MessageBox.Show("Finsh");
    }
}

Это происходит после одного выполнения (sqlCommand.ExecuteNonQuery();), выполнение которого занимает более 10 минут. Я не получаю никакого исключения в этом пункте, в следующем исключении я получаю это исключение:

The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements.

Это связано с тем, что для System.Transactions.TransactionManager.MaximumTimeout установлено значение TimeSpan, равное 10 минутам.

Я ищу, и я обнаружил, что, возможно, это связано с «System.Transactions -> maxTimeout machine.config», но я получаю исключение после изменения моей конфигурации в этот файл:

<?xml version="1.0"?>
<configuration> 
  <system.transactions>
    <machineSettings maxTimeout="10:00:00"/>
  </system.transactions> 
  <appSettings>    
    <add key="FileName" value="MyFileName" />
    <add key="MySpace" value="5 MB" />       
    <add key="ClientSettingsProvider.ServiceUri" value="" />        
  </appSettings>      
</configuration>

когда я пытаюсь получить System.Transactions.TransactionManager.MaximumTimeout во время выполнения после того, как я изменил файл конфигурации, я получаю это исключение:

"Система конфигурации не инициализирована"

Кто-нибудь знает, как решить эту проблему?

(Общее примечание о моем случае: мне нужно выполнить хранимую процедуру, которая занимает около 20 минут, потому что мне нужно преобразовать таблицу, содержащую int, в bigint в SQL (int = 32 бит, bigint = 64 бит). Мне нужно создать новые таблицы и вставить данные из старой таблицы в новую таблицу с int64.Таблица, связанная идентификатором с другими 4 таблицами, каждая из которых содержит более 20 миллионов строк, а также привязку,индексацию и многое другое.Я не могу разделить эту процедуру на небольшую хранимую процедуру, поэтому мне нужно изменить максимальное время ожидания на один час или более, 10 минут недостаточно!).


person user436862    schedule 19.06.2011    source источник
comment
Хранимые процедуры, которые занимают 20 минут? В этом проблема.   -  person Grant Thomas    schedule 19.06.2011
comment
Атомарная операция должна быть атомарной, независимо от того, занимает она много времени или нет. Конечно, г-н Д., во многих случаях вы могли бы спроектировать свое приложение для выполнения операций в памяти или каком-либо другом кэше, а затем как можно быстрее выполнять все транзакции в базе данных, но это часто нецелесообразно. Если вы используете базу данных в качестве источника информации, и ваша транзакция включает в себя внесение изменений в эту модель данных (например, с помощью подкласса DataContext), от которых будут зависеть будущие вычисления в транзакции, то многие из вас законно заканчивают транзакцией. это занимает › 10 минут.   -  person Triynko    schedule 22.11.2011


Ответы (6)


Если вы не боитесь использовать отражение, вы можете программно переопределить максимальное время ожидания. Не гарантируется, что этот код будет соответствовать требованиям завтрашнего дня, но он работает, начиная с .NET 4.0.

public static class TransactionmanagerHelper
{
    public static void OverrideMaximumTimeout(TimeSpan timeout)
    {
        //TransactionScope inherits a *maximum* timeout from Machine.config.  There's no way to override it from
        //code unless you use reflection.  Hence this code!
        //TransactionManager._cachedMaxTimeout
        var type = typeof(TransactionManager);
        var cachedMaxTimeout = type.GetField("_cachedMaxTimeout", BindingFlags.NonPublic | BindingFlags.Static);
        cachedMaxTimeout.SetValue(null, true);

        //TransactionManager._maximumTimeout
        var maximumTimeout = type.GetField("_maximumTimeout", BindingFlags.NonPublic | BindingFlags.Static);
        maximumTimeout.SetValue(null, timeout);
    }
}

Вы можете использовать его следующим образом:

            TransactionmanagerHelper.OverrideMaximumTimeout(TimeSpan.FromMinutes(30));
person Matt Honeycutt    schedule 03.07.2012
comment
Просто попробовал это, похоже, в коде примера есть ошибка. 'timeout' необходимо передать в SetValue для максимального времени ожидания, а не для Timespan.FromMinutes(30). - person Adrian Russell; 06.10.2014
comment
Это хороший хак! Спасибо - person NeutronCode; 28.07.2015
comment
@AdrianRussell Обновил ответ - person buckley; 08.04.2016

Вы не можете указать machineSettings в своем собственном файле конфигурации, но вам необходимо фактически изменить/добавить machineSettings\maxTimeout в machine.config файле компьютера.

Существует один экземпляр этого файла для каждой комбинации 32/64-разрядной версии и версии CLR на вашем компьютере. Например, файл 32-разрядной версии для .NET 2.0 находится в каталоге %windir%\Microsoft.NET\Framework\v2.0.50727\CONFIG. Файл для 64-битного приложения .NET 2.0 находится в каталоге %windir%\Microsoft.NET\Framework64\v2.0.50727\CONFIG. Аналогично, если вы используете .NET 4.0, вам нужно изменить файл в подкаталоге v4.0.30319\Config.

Обратите внимание, что, изменяя этот файл (как следует из названия), вы изменяете максимальное время ожидания для каждой транзакции в вашем ящике. Так что будьте осторожны, что вы устанавливаете здесь. В вашем примере вы бы изменили тайм-аут на 10 (!) часов.

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

В любом случае, отдельные приложения могут по-прежнему устанавливать свой собственный максимальный тайм-аут, который, однако, всегда ограничен значением в файле machine.config, используя раздел system.transactions в своем собственном файле конфигурации:

  <system.transactions>
    <defaultSettings timeout="22:00:00"/>
  </system.transactions>

Обратите внимание, что имя элемента здесь defaultSettings, а не machineSettings.

Вы также можете проверить следующие ссылки для получения дополнительной информации:

person Christian.K    schedule 19.06.2011
comment
Спасибо, но все же, потому что defaultSetting ограничен maxTimeout, и я попытался установить maxTimeout для моего machineSetting (machine.config), я все еще получаю исключение: система конфигурации не инициализирована (внутреннее исключение: {Использование раздела является ошибкой зарегистрирован как allowExeDefinition='MachineOnly' за пределами machine.config. (C:\\...Aplication{ath\ApplicationName.exe.Config строка 5)}. Как я могу решить эту проблему? Также, если я не хочу менять другие настройки приложения, только в моем приложении.... есть ли способ сделать это? - person user436862; 20.06.2011
comment
Опять же, похоже, что вы пытаетесь поместить элемент machineSetting-Element в свой локальный файл конфигурации, он даже говорит в сообщении об исключении ('C:\\...Aplication{ath\ApplicationName.exe .Конфигурация, строка 5'). Поместите его только в свой файл «machine.config» (см. первый абзац моего ответа), и все будет хорошо. - person Christian.K; 20.06.2011

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

eg,

 t.Complete(); 

предшествует

            if (sqlComm != null)
                sqlComm.Dispose();  

ваш t.complete(); действительно нужно перейти к концу вашего сегмента sql. Я не запускал это, чтобы доказать, что это работает, но это имеет смысл, поскольку у вас есть раздел транзакций, который затем выполняет дальнейшую работу после того, как вы сказали, что он завершен.

Я использовал это как ссылку: http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx

person BugFinder    schedule 19.06.2011
comment
Спасибо, но все же я получаю то же исключение... Любая другая идея? - person user436862; 20.06.2011

Попробуйте изменить порядок элементов в файле конфигурации так, чтобы appSettings стоял перед system.transactions, например:

<appSettings>    
  ...     
</appSettings> 
<system.transactions>
  ...
</system.transactions> 

Кроме того, если у вас есть configSections, сделайте то же самое.

person Grant Thomas    schedule 19.06.2011
comment
Я пытался, но при инициализации возникает то же самое исключение: система конфигурации не инициализировалась ... Я действительно пробовал все, но безуспешно ... Есть идеи? - person user436862; 20.06.2011
comment
Я получал такое же исключение конфигурации, но когда я переместил тег system.transactions в конец файла machine.config, все заработало, как и ожидалось! - person Thomas C. G. de Vilhena; 18.04.2013
comment
Спасибо, @Thomas C.G. de Vilhena, перемещение тега system.transactions вниз помогло и мне! - person 03Usr; 29.04.2013

Значение по умолчанию:

Transaction Binding=Implicit Unbind. 

Implicit Unbind приводит к тому, что соединение отключается от транзакции, когда она завершается. В редких случаях люди используют:

Binding=Explicit Unbind

Вы можете проверить это в своем случае.

person Abhijit Kumar    schedule 02.07.2013

Попробуйте изменить свойство machine.config allowExeDefinition на «MachineToApplication»:

<sectionGroup name="system.transactions" type="System.Transactions.Configuration.TransactionsSectionGroup, System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null">
            <section name="defaultSettings" type="System.Transactions.Configuration.DefaultSettingsSection, System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null"/>
            <section name="machineSettings" type="System.Transactions.Configuration.MachineSettingsSection, System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, Custom=null" allowDefinition="MachineOnly" allowExeDefinition="MachineToApplication" />
</sectionGroup>

И после этого измените файл web.config/app.config:

<?xml version="1.0" encoding="utf-8"?>
   <configuration>
    ...
    <system.transactions>
        <machineSettings maxTimeout="00:00:00" ></machineSettings>
      </system.transactions>
    </configuration>

Значение 00:00:00 (или ноль) в свойстве «maxTimeout» интерпретируется как бесконечность.

С уважением,

person Michael    schedule 10.09.2013