TransactionScope automatycznie eskalacja do MSDTC na niektórych maszynach?

W naszym projekcie używamy TransactionScope, aby zapewnić, że nasza warstwa dostępu do danych wykonuje swoje akcje w transakcji. Naszym celem jest Nie Wymaganie włączenia usługi MSDTC na maszynach naszych użytkowników końcowych.

Problem polega na tym, że na połowie naszych maszyn programistycznych możemy działać z wyłączonym MSDTC. Druga połowa musi mieć włączone lub dostać "MSDTC na [serwer] jest niedostępny" komunikat o błędzie.

To naprawdę mnie drapie po głowie i ma mnie poważnie rozważając powrót do domowego rozwiązania transakcyjnego opartego na ADO.NET obiekty transakcji. To pozornie szaleństwo - ten sam kod, który działa (i nie eskaluje) na połowie naszych programistów , eskaluje na innych programistów.]}

Liczyłem na lepszą odpowiedź na prześledzenie, dlaczego transakcja jest eskalowana do DTC , ale niestety tak nie jest.

Oto przykładowy fragment kodu, który spowoduje kłopoty, na maszynach, które próbują aby 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ę się dokopaliśmy i próbowaliśmy to rozgryźć. Oto kilka informacji na temat maszyn, na których działa:
  • Dev 1: Windows 7 x64 sql2008
  • Dev 2: Windows 7 x86 sql2008
  • Dev 3: Windows 7 x64 SQL2005 SQL2008

Programiści, na których nie działa:

  • Dev 4: Windows 7 x64, SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • mój komputer domowy: Windows Vista Home Premium, x86, SQL2005

Powinienem dodać, że wszystkie maszyny, W celu wytropienia problemu, zostały w pełni załatane ze wszystkim, co jest dostępne z Microsoft Update.

Update 1:

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

  1. co najmniej jeden trwały zasób, który nie obsługuje powiadomień jednofazowych, jest uwzględniony w transakcji.
  2. do transakcji włączono co najmniej dwa trwałe zasoby obsługujące powiadomienia jednofazowe. Na przykład nawiązanie pojedynczego połączenia nie powoduje promocji transakcji. Jednakże, za każdym razem, gdy otwierasz drugie połączenie z bazą danych, powodując, że baza danych się zaciąga, System.Infrastruktura transakcji wykrywa, że jest to drugi trwały zasób w transakcji i eskaluje go do transakcji MSDTC.
  3. wywoływane jest żądanie "marshal" transakcji do innej domeny aplikacji lub innego procesu. Na przykład serializacja obiektu transakcji przez granicę domeny aplikacji. Obiekt transakcji jest skalowany według wartości, oznacza to, że każda próba przekroczenia granicy domeny aplikacji (nawet w tym samym procesie) powoduje serializację obiektu transakcji. Obiekty transakcji można przekazać poprzez wywołanie zdalnej metody, która przyjmuje transakcję jako parametr lub można spróbować uzyskać dostęp do zdalnego komponentu obsługującego transakcje. To serializuje obiekt transakcji i powoduje eskalację, tak jak w przypadku transakcji serializowanej w domenie aplikacji. Jest rozprowadzany i lokalny Menedżer transakcji nie jest już odpowiedni.
Nie doświadczamy #3. #2 nie dzieje się, ponieważ istnieje tylko jedno połączenie na raz, a także do jednego "trwałego zasobu". Czy jest jakiś sposób, aby #1 mógł się dziać? Jakaś konfiguracja SQL2005 / 8, która powoduje, że nie obsługuje powiadomień jednofazowych?

Update 2:

Ponownie zbadane, osobiście, wszystkich wersji SQL Server - "Dev 3" faktycznie ma SQL2008, a "Dev 4" jest właściwie SQL2005. To nauczy mnie już nigdy nie ufać moim współpracownikom. ;) Z powodu tej zmiany danych, jestem prawie pewien, że znaleźliśmy nasz problem. Nasi programiści SQL2008 nie doświadczają problemu, ponieważ SQL2008 ma spore ilości niesamowite zawarte, że SQL2005 nie ma.

To również mówi mi, że ponieważ będziemy wspierać SQL2005, że nie możemy używać TransactionScope jak byliśmy, i jeśli chcemy używać TransactionScope będziemy musieli przekazać pojedynczy obiekt SqlConnection wokół...co wydaje się problematyczne w sytuacjach, gdy SqlConnection nie może być łatwo przekazane around...it po prostu pachnie instancją global - SqlConnection. Pew!

Update 3

Aby wyjaśnić tutaj w pytaniu:

SQL2008:

  • pozwala na wiele połączeń w ramach jednego obszaru transakcyjnego (jak pokazano w powyższym przykładowym kodzie.)
  • Zastrzeżenie # 1: Jeśli te wiele połączeń SQL jest zagnieżdżonych, to znaczy dwa lub więcej SqlConnections są otwierane w tym samym czasie, TransactionScope natychmiast eskaluje do DTC.
  • W przeciwieństwie do innych serwerów SQL, SQL connection może być używany jako serwer bazujący na SQL connection, który może być używany jako serwer bazujący na SQL connection.]}

SQL2005:

  • nie pozwala na wiele połączeń w ramach jednej transakcji, kropka. Spowoduje to eskalację, gdy/jeśli zostanie otwarta druga sekcja SqlConnection.

Update 4

In zainteresowanie uczynieniem tego pytania jeszcze bardziej bałaganem użytecznym, i tylko dla większej jasności, oto jak możesz zmusić SQL2005 do eskalacji do DTC z pojedynczym SqlConnection:

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}
Wydaje mi się, że to zepsute, ale chyba rozumiem, czy każde połączenie do {3]} jest przechwytywane z puli połączeń.

"Dlaczego tak się stało?" Cóż, jeśli użyjesz sqltableadapter przeciwko temu połączeniu przed jego otwarciem, sqltableadapter otworzy się i zamknij połączenie, skutecznie kończąc transakcję za Ciebie, ponieważ teraz nie możesz go ponownie otworzyć.

Tak więc, zasadniczo, aby z powodzeniem używać TransactionScope z sql2005, musisz mieć jakiś globalny obiekt połączenia, który pozostaje otwarty od momentu utworzenia pierwszej instancji TransactionScope, dopóki nie będzie już potrzebny. Poza zapachem kodu obiektu połączenia globalnego, otwarcie połączenia jako pierwsze i zamknięcie go jako ostatnie jest sprzeczne z logiką otwarcia połączenie tak późno, jak to możliwe i zamknięcie go tak szybko, jak to możliwe.

Author: Community, 2009-11-07

6 answers

SQL Server 2008 może używać wielu SQLConnection s W Jednym TransactionScope bez eskalacji, pod warunkiem, że połączenia nie są otwarte w tym samym czasie, co skutkowałoby wieloma "fizycznymi" połączeniami TCP i tym samym wymagałoby eskalacji.

Widzę, że niektórzy z Twoich programistów mają SQL Server 2005, a inni mają SQL Server 2008. Czy jesteś pewien, że prawidłowo zidentyfikowałeś, które z nich eskalują, a które nie?

Najbardziej oczywistym wyjaśnieniem byłoby to, że programiści z SQL Server 2008 są te, które się nie nasilają.

 70
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-25 05:06:47
 53
Author: Peter Meinl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-23 07:15:56

Ten kod spowoduje eskalację podczas łączenia się z 2005.

Sprawdź dokumentację na MSDN- http://msdn.microsoft.com/en-us/library/ms172070.aspx

Transakcje promocyjne w SQL Server 2008

W wersji 2.0. Net Framework i SQL Server 2005, otwierając drugi połączenie wewnątrz TransactionScope automatycznie Promuje transakcja do pełnej dystrybucji transakcja, nawet jeśli obie połączenia używali identycznego połączenia struny. W tym przypadku dystrybuowane transakcja dodaje niepotrzebnych kosztów to zmniejsza wydajność.

Począwszy od SQL Server 2008 i Wersja 3.5. Net Framework, transakcje lokalne nie są już promowane do transakcji rozproszonych jeśli zostanie otwarte inne połączenie w transakcja po poprzednim transakcja zamknięta. Wymaga to żadnych zmian w kodzie, jeśli jesteś już za pomocą połączenia pooling i zaciąganie się do transakcji.

Nie mogę wyjaśnić, dlaczego Dev 3: Windows 7 x64, SQL2005 powiodło się, a Dev 4: Windows 7 x64 zawodzi. Jesteś pewien, że nie jest odwrotnie?

 31
Author: hwiechers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-07 17:33:54

Nie wiem, dlaczego ta odpowiedź została usunięta, ale wydaje się, że zawiera pewne istotne informacje.

Odpowiedział Sie 4 '10 o 17: 42 Eduardo

  1. Ustaw Enlist=false na łańcuchu połączeń, aby uniknąć automatycznego zaciągania przy transakcji.

  2. Ręczne pozyskiwanie połączenia jako uczestników w zakresie transakcji. [oryginalny artykuł Nieaktualny] lub zrób to: Jak zapobiec automatycznej promocji MSDTC [archive.is]

 8
Author: Chris Marisic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:09

Nie jestem pewien, czy problemem jest zagnieżdżone połączenie. Wywołuję lokalną instancję SQL server i nie generuje 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();
        }
    }
 2
Author: Iftikhar Ali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 13:14:51

TransactionScope zawsze eskaluje do transakcji DTC, jeśli używasz dostępu więcej niż 1 połączenia wewnątrz. Jedynym sposobem, w jaki powyższy kod może działać z wyłączonym DTC, jest uzyskanie tego samego połączenia z puli połączeń za każdym razem.

"problem polega na tym, że na połowie naszych maszyn programistycznych możemy działać z wyłączonym MSDTC." Czy na pewno jest wyłączony;)

 1
Author: amateur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-07 00:28:12