TransactionScope автоматически перерастает в MSDTC на некоторых машинах?


в нашем проекте мы используем TransactionScope для обеспечения того, чтобы наш уровень доступа к данным выполнял свои действия в транзакции. Мы стремимся к не требуйте, чтобы служба MSDTC была включена на машинах наших конечных пользователей.

проблема в том, что на половине наших машин разработчиков мы можем работать с отключенным MSDTC. Другая половина должна иметь его включен или они получают "MSDTC на [сервере] недоступен" сообщение об ошибке.

это действительно заставило меня чесаться моя голова и я серьезно рассматриваю возможность отката к домашнему решению TransactionScope, основанному на ADO.NET объекты транзакций. Это, казалось бы, безумие - тот же код, который работает (и не обостряется) на половине нашего разработчика тут эскалация на другого разработчика.

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

вот примерный бит кода, который вызовет проблемы, на машинах, которые пытаются эскалации, он пытается эскалации на втором соединении.Open () (и да, в это время нет другого соединения.)

using (TransactionScope transactionScope = new TransactionScope() {
   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();
         using (SqlDataReader reader = command.ExecuteReader()) {
            // use the reader
            connection.Close();
         }
      }
   }

   // Do other stuff here that may or may not involve enlisting 
   // in the ambient transaction

   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();  // Throws "MSDTC on [SERVER] is unavailable" on some...

         // gets here on only half of the developer machines.
      }
      connection.Close();
   }

   transactionScope.Complete();
}

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

  • Dev 1: Windows 7 x64 SQL2008
  • Dev 2: Windows 7 x86 SQL2008
  • Dev 3: Windows 7 x64 SQL2005 SQL2008

разработчики это не работает на:

  • Dev 4: Windows 7 x64,SQL2008SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • мой домашний ПК: Windows Vista Home Premium, x86, SQL2005

Я должен добавить, что все машины, в попытке выследить проблему, были полностью установлены все это доступно из Центра обновления Майкрософт.

обновление 1:

на странице MSDN transaction-escalation указано, что следующие условия приведут к эскалации транзакции до DTC:

  1. по крайней мере один надежный ресурс, который не поддерживает однофазные уведомления, включается в транзакцию.
  2. по крайней мере два надежных ресурса, которые поддерживают однофазный уведомления зачисляются в транзакцию. Например, присоединение к одному соединению не приводит к продвижению транзакции. Однако всякий раз, когда вы открываете второе соединение с базой данных, вызывая базу данных для подключения, система.Инфраструктура транзакций обнаруживает, что это второй долговременный ресурс в транзакции, и преобразует его в транзакцию MSDTC.
  3. запрос "маршал" сделки в другой домен приложения или процесс вызванный. Например, сериализация объекта транзакции через границу домена приложения. Объект транзакции маршалируется по значению, что означает, что любая попытка передать его через границу домена приложения (даже в том же процессе) приводит к сериализации объекта транзакции. Вы можете передать объекты транзакций, вызвав удаленный метод, который принимает транзакцию в качестве параметра, или попытаться получить доступ к удаленному компоненту, обслуживаемому транзакциями. Этот сериализует объект транзакции и приводит к эскалации, как при сериализации транзакции в домене приложения. Он распространяется, и локальный менеджер транзакций больше не является адекватным.

мы не испытываем #3. #2 не происходит, потому что есть только одно соединение за раз, и это также один "прочный ресурс". Есть ли способ, что № 1 может быть? Некоторые конфигурации SQL2005 / 8, которые не поддерживают его однофазные уведомления?

обновление 2:

повторно исследованы, лично, все версии SQL Server - "Dev 3" На самом деле имеет SQL2008, а "Dev 4" на самом деле SQL2005. Это научит меня не доверять своим коллегам. ;) Из-за этого изменения в данных, я уверен, что мы нашли нашу проблему. Наши разработчики SQL2008 не испытывали проблемы, потому что SQL2008 имеет обильное количество удивительных включены, что SQL2005 не имеет.

Он также говорит мне, что, поскольку мы собираемся поддерживать SQL2005, мы не можем использовать TransactionScope, как раньше, и если мы хотим использовать TransactionScope, нам нужно будет передать один объект SqlConnection...что представляется проблематичным в тех ситуациях, когда объект sqlconnection не могут легко быть переданы вокруг...он просто пахнет глобальных-экземпляр sqlconnection. Пью!

обновление 3

просто чтобы прояснить здесь, в вопрос:

SQL2008:

  • позволяет несколько соединений в пределах одного TransactionScope (как показано в приведенном выше примере кода.)
  • предостережение #1: Если эти несколько SqlConnections являются вложенными, то есть два или более SqlConnections открываются одновременно, TransactionScope немедленно перерастет в DTC.
  • предостережение #2: Если дополнительный SqlConnection открыт для другого 'устойчивый ресурс' (т. е.: другой SQL Server,) он сразу же перерастет в DTC

SQL2005:

  • не разрешает несколько соединений в пределах одного TransactionScope, период. Он будет увеличиваться при открытии / открытии второго соединения SqlConnection.

обновление 4

в интересах сделать этот вопрос еще более беспорядок полезно, и просто для большей ясности, вот как вы можете получить SQL2005 для эскалации в DTC с помощью одинSqlConnection:

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

Это просто кажется сломанным для меня, но я думаю, что могу понять, если каждый вызов SqlConnection.Open() захватывает из пула соединений.

6 272

6 ответов:

среда SQL Server 2008 может использовать несколько SQLConnectionв одном TransactionScope без эскалации, при условии, что соединения не открыты одновременно, что приведет к нескольким "физическим" TCP-соединениям и, следовательно, потребует эскалации.

Я вижу, что некоторые из ваших разработчиков имеют SQL Server 2005, а другие-SQL Server 2008. Вы уверены, что правильно определили, какие из них растут, а какие нет?

наиболее очевидным объяснением было бы то, что разработчики с SQL Сервер 2008-это те, которые не растут.

код будет вызвать эскалацию при подключении к 2005 году.

проверьте документацию на MSDN -http://msdn.microsoft.com/en-us/library/ms172070.aspx

расширяемые транзакции в SQL Server 2008

в версии 2.0 платформы .NET Framework и SQL Server 2005, открывая второй соединение внутри TransactionScope автоматически повысил бы транзакция в полном объеме распределенный сделки, даже если оба соединения использовали одинаковое соединение веревка. В этом случае распределенный транзакция добавляет ненужные накладные расходы это снижает производительность.

начиная с SQL Server 2008 и версия 3.5 платформы .NET Framework, локальные транзакции больше не выполняются повышен до распределенных транзакций если в системе открыто другое соединение сделка после окончания предыдущего сделка закрыта. Это требует никаких изменений в вашем код если вы уже используя пул соединений и участие в сделках.

Я не могу объяснить, почему Dev 3: Windows 7 x64, SQL2005 успешно и Dev 4: Windows 7 x64 терпит неудачу. Вы уверены, что не наоборот?

Я не знаю, почему этот ответ был удален, но это, кажется, есть некоторые соответствующую информацию.

ответил 4 августа '10 в 17:42 Эдуардо

  1. Set Enlist=false в строке подключения, чтобы избежать автоматического зачисления на транзакцию.

  2. вручную подключить соединение в качестве участников операции в области. [Оригинал статьи устарела] или сделайте так:как предотвратить автоматическое продвижение MSDTC [архиве.это]

Я не слишком уверен, если вложенное соединение является проблемой. Я вызываю локальный экземпляр SQL server, и он не генерирует DTC??

    public void DoWork2()
    {
        using (TransactionScope ts2 = new TransactionScope())
        {
            using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;"))
            {
                SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                cmd.Connection = conn1;
                cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100"))
                {
                    cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                    cmd.Connection = conn2;
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }

            ts2.Complete();
        }
    }

TransactionScope всегда перерастает в транзакцию DTC, если вы используете доступ более чем к 1 соединению внутри. Единственный способ, которым приведенный выше код может работать с отключенным DTC, - это если вы оба раза получаете одно и то же соединение из пула соединений.

"проблема в том, что на половине наших машин разработчиков мы можем работать с отключенным MSDTC." Вы уверены, что он отключен ;)