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

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

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

Это действительно заставило меня почесывать голову, и я серьезно подумываю о том, чтобы вернуться к основанному на транзакциях решению 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, SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • Мой домашний компьютер: Windows Vista Home Premium, x86, SQL2005

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

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

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

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

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

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

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

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

Обновление 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() захватывает пул соединений.

"Почему это может произойти?" Ну, если вы используете SqlTableAdapter против этого соединения до его открытия, SqlTableAdapter откроет и закроет соединение, фактически завершив транзакцию для вас, потому что теперь вы не можете повторно открыть его.

Итак, в основном, для успешного использования TransactionScope с SQL2005 вам нужно создать какой-то объект глобального соединения, который остается открытым с момента первого TransactionScope, созданный до тех пор, пока он больше не понадобится. Помимо запаха кода глобального объекта соединения, сначала открывая соединение и закрывая его, он находится в противоречии с логикой открытия соединения как можно позже и как можно скорее закрывает его.

Ответ 1

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

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

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

Ответ 3

Этот код вызовет эскалацию при подключении к 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

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

ответил 4 авг '10 в 17:42 Eduardo

Ответ 5

Я не уверен, что проблема с вложенным соединением. Я вызываю локальный экземпляр SQL-сервера и не генерирует 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();
        }
    }

Ответ 6

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

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

Ответ 7

Убедитесь, что ваша connectionString не устанавливает для пула значение false. Это приведет к новому соединению для каждого нового SqlConnection в TransactionScope и преобразует его в DTC.