TransactionScope MaximumTimeout - PullRequest
       42

TransactionScope MaximumTimeout

15 голосов
/ 19 июня 2011

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

Ответы [ 6 ]

29 голосов
/ 03 июля 2012

Если вы не боитесь использовать отражение, вы можете программно переопределить максимальное время ожидания. Этот код не гарантирован для будущего, но он работает с .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));
12 голосов
/ 19 июня 2011

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

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

0 голосов
/ 10 сентября 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" интерпретируется как бесконечность.

С уважением,

0 голосов
/ 02 июля 2013

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

Transaction Binding=Implicit Unbind. 

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

Binding=Explicit Unbind

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

0 голосов
/ 19 июня 2011

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

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

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

0 голосов
/ 19 июня 2011

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

например,

 t.Complete(); 

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

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

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

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

...