TransactionScope automatycznie eskaluje do MSDTC na niektórych komputerach?

W naszym projekcie używamy TransactionScope, aby zapewnić, że nasza warstwa dostępu do danych wykonuje swoje działania w transakcji. Dążymy donie wymagać włączenia usługi MSDTC na naszych komputerach użytkowników końcowych.

Problem polega na tym, że w połowie naszych maszyn programistycznych możemy uruchomić z wyłączoną funkcją MSDTC. Druga połowa musi mieć włączoną tę funkcję lub musi uzyskać„MSDTC na [SERWER] jest niedostępny” Komunikat o błędzie.

To naprawdę sprawiło, że drapałem się po głowie i poważnie zastanawiam się nad wycofaniem się do rozwiązania typu TransactionScope podobnego do home-spun opartego na obiektach transakcyjnych ADO.NET. To wydaje się szalone - ten sam kod, który działa (i nie nasila się) na połowie naszego deweloperarobi eskalować na innych programistach.

Liczyłem na lepszą odpowiedźŚledź przyczynę eskalacji transakcji do DTC ale niestety tak nie jest.

Oto przykładowy kod, który spowoduje problem, na komputerach, które próbują eskalować, próbuje eskalować na drugim połączeniu. Open () (i tak, nie ma innego połączenia otwartego w tym czasie).

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();
}

Naprawdę kopaliśmy i próbowaliśmy to rozgryźć. Oto kilka informacji na temat maszyn, na których działa:

Dev 1: Windows 7 x64 SQL2008Dev 2: Windows 7 x86 SQL2008Dev 3: Windows 7 x64SQL2005 SQL2008

Deweloperzy nie działają na:

Dev 4: Windows 7 x64,SQL2008 SQL2005Dev 5: Windows Vista x86, SQL2005Dev 6: Windows XP X86, SQL2005My Home PC: Windows Vista Home Premium, x86, SQL2005

Powinienem dodać, że wszystkie komputery, starając się rozwiązać problem, zostały w pełni załatane za pomocą wszystkiego, co jest dostępne w witrynie Microsoft Update.

Aktualizacja 1:http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/ opisuje podobny problem ... w 2006 roku!http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - przeczytaj ten przykładowy kod, który wyraźnie pokazuje zagnieżdżone drugie połączenie (w rzeczywistości z drugim serwerem SQL), które eskaluje do DTC.Nie robimy tego w naszym kodzie - nie używamy różnych serwerów SQL, ani różnych ciągów połączeń, ani nie otwieramy zagnieżdżonych połączeń dodatkowych -nie powinno być eskalacji do DTC.http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx (od 2005 r.) mówi o tym, jak eskalacja do DTC zawsze będzie miała miejsce podczas łączenia się z SQL2000. Używamy SQL2005 / 2008http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN przy eskalacji transakcji.

Ta strona eskalacji transakcji MSDN stwierdza, że ​​następujące warunki spowodują eskalację transakcji do DTC:

W transakcji jest rejestrowany co najmniej jeden trwały zasób, który nie obsługuje powiadomień jednofazowych.W transakcji są rejestrowane co najmniej dwa trwałe zasoby, które obsługują powiadomienia jednofazowe. Na przykład wpisanie pojedynczego połączenia nie powoduje promowania transakcji. Jednak za każdym razem, gdy otwierasz drugie połączenie z bazą danych powodującą rejestrację bazy danych, infrastruktura System.Transactions wykrywa, że ​​jest to drugi trwały zasób w transakcji i eskaluje go do transakcji MSDTC.Wywoływana jest prośba o „przeniesienie” transakcji do innej domeny aplikacji lub innego procesu. Na przykład serializacja obiektu transakcji na granicy domeny aplikacji. Obiekt transakcji jest połączony przez wartość, co oznacza, że ​​każda próba przekazania go przez granicę domeny aplikacji (nawet w tym samym procesie) powoduje serializację obiektu transakcji. Możesz przekazać obiekty transakcji, wykonując wywołanie na zdalnej metodzie, która przyjmuje transakcję jako parametr lub możesz spróbować uzyskać dostęp do zdalnego składnika obsługującego transakcje. Powoduje to szeregowanie obiektu transakcji i powoduje eskalację, tak jak w przypadku szeregowania transakcji w domenie aplikacji. Jest dystrybuowany, a lokalny menedżer transakcji nie jest już odpowiedni.

Nie doświadczamy # 3. # 2 się nie dzieje, ponieważ istnieje tylko jedno połączenie na raz, a także jedno trwałe źródło. Czy jest jakiś sposób, w jaki może się zdarzyć # 1? Niektóre konfiguracje SQL2005 / 8 powodujące, że nie obsługuje powiadomień jednofazowych?

Aktualizacja 2:

Ponownie zbadany osobiście każdy z wersji SQL Server - „Dev 3” faktycznie ma SQL2008, a „Dev 4” to właściwie SQL2005. To nauczy mnie nigdy więcej nie ufać moim współpracownikom. ;) Z powodu tej zmiany danych jestem całkiem pewien, że znaleźliśmy nasz problem. Nasi programiści SQL2008 nie doświadczyli problemu, ponieważ SQL2008 zawiera wiele niesamowitych dołączonych elementów, których SQL2005 nie ma.

Mówi mi także, że ponieważ będziemy obsługiwać SQL2005, nie możemy używać TransactionScope, tak jak byliśmy, i jeśli chcemy użyć TransactionScope, będziemy musieli przekazać pojedynczy obiekt SqlConnection wokół ... co wydaje się problematyczne w sytuacjach, w których SqlConnection nie może być łatwo przekazane ... po prostu pachnie instancją global-SqlConnection. Ławka w kościele!

Aktualizacja 3

Aby wyjaśnić tutaj pytanie:

SQL2008:

Umożliwia wiele połączeń w ramach jednego TransactionScope (jak pokazano w powyższym przykładowym kodzie).Zastrzeżenie nr 1: Jeśli te wiele SqlConnections jest zagnieżdżonych, to znaczy dwa lub więcej SqlConnections są otwierane w tym samym czasie, TransactionScope natychmiast eskaluje do DTC.Zastrzeżenie 2: Jeśli dodatkowe SqlConnection zostanie otwarte dla innego„trwały zasób” (tj .: inny serwer SQL), natychmiast przekształci się w DTC

SQL2005:

Nie zezwala na wiele połączeń w pojedynczym okresie TransactionScope. Będzie eskalować, gdy zostanie otwarte drugie połączenie SqlConnection.Aktualizacja 4

W celu uczynienia tego pytania jeszcze bardziejbałaganu przydatne, i tylko dla większej jasności, oto jak możesz uzyskać SQL2005 eskalacji do DTC za pomocąpojedynczy SqlConnection:

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

Wydaje mi się to po prostu zepsute, ale myślę, że mogę zrozumieć, czy każde wezwanieSqlConnection.Open() chwyta z puli połączeń.

„Ale dlaczego tak się stanie?” Cóż, jeśli użyjesz SqlTableAdapter przeciwko temu połączeniu przed jego otwarciem, SqlTableAdapter otworzy i zamknie połączenie, skutecznie kończąc transakcję dla ciebie, ponieważ teraz nie możesz go ponownie otworzyć.

Tak więc, w celu pomyślnego użycia TransactionScope z SQL2005, musisz mieć jakiś obiekt globalnego połączenia, który pozostaje otwarty z punktu pierwszego instancji TransactionScope, dopóki nie będzie już potrzebny. Poza zapachem kodu globalnego obiektu połączenia, otwarcie najpierw połączenia i zamknięcie go jest sprzeczne z logiką jak najszybszego otwarcia połączenia i zamknięcia go jak najszybciej.

questionAnswers(6)

yourAnswerToTheQuestion