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 SQL2008Dev 2: Windows 7 x86 SQL2008Dev 3: Windows 7 x64SQL2005 SQL2008

Разработчики, на которых это не работает:

Dev 4: Windows 7 x64,SQL2008 SQL2005Dev 5: Windows Vista x86, SQL2005Дев 6: Windows XP X86, SQL2005Мой домашний ПК: Windows Vista Home Premium, x86, SQL2005

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

Обновление 1:http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/ описывает подобную проблему ... еще в 2006 году!http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - прочитайте этот пример кода, он ясно демонстрирует соединение с вложенной секундой (фактически, со вторым сервером SQL), которое переходит в DTC.Мы не делаем это в нашем коде - мы не используем ни разные SQL-серверы, ни разные строки подключения, а также не открываем вложенные вторичные подключения -не должно быть эскалации к DTC.http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx (с 2005 года) рассказывает о том, как эскалация к DTC всегда будет происходить при подключении к SQL2000. Мы используем SQL2005 / 2008http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN об эскалации транзакций.

На этой странице эскалации транзакций 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-сервер), он немедленно переходит в 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 до тех пор, пока он больше не понадобится. Помимо запаха кода глобального объекта соединения, открытие первого соединения и его закрытие последним противоречит логике открытия соединения как можно позже и его закрытия как можно скорее.

Ответы на вопрос(6)

Ваш ответ на вопрос