TransactionScope maximumTimeout

Я использую 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-- > machine.config maxTimeout", но я получаю исключение после изменения моей конфигурации в этот файл:

<?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 = 32bit, bigint =64). Мне нужно создать новые таблицы и вставить данные из старой таблицы в новую таблицу с помощью int64. Таблица, связанная идентификатором с другими 4 таблицами, каждая содержит более 20 миллионов строк, а также привязка, индексирование и многое другое. Я не могу разделить эту процедуру на небольшую хранимую процедуру, поэтому мне нужно изменить максимальный тайм-аут на один час или более, 10 минут недостаточно!).

Ответ 1

Если вы не боитесь использовать отражение, вы можете фактически переопределить максимальный тайм-аут программным путем. Этот код не гарантируется future- proof, но он работает с .NET 4.0.

public static class TransactionmanagerHelper
{
    public static void OverrideMaximumTimeout(TimeSpan timeout)
    {
        //TransactionScope inherits a *maximum* timeout from Machine.config.  There 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));

Ответ 2

Вы не можете указать 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 not machineSettings.

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

Ответ 3

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

например,

 t.Complete(); 

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

Transaction Binding=Implicit Unbind. 

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

Binding=Explicit Unbind

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

Ответ 6

Попробуйте изменить свойство 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" интерпретируется как бесконечность.

Привет,