W naszym projekcie używamy TransactionScope's, aby zapewnić, że nasza warstwa dostępu do danych wykonuje swoje działania w transakcji. Naszym celem jest nie wymagać, aby usługa MSDTC była włączona na naszych urządzeniach użytkowników końcowych.
Problem w tym, że na połowie maszyn naszych programistów możemy pracować z wyłączonym MSDTC. Druga połowa musi mieć to włączone lub otrzymają komunikat o błędzie „MSDTC na [SERWER] jest niedostępny” .
Naprawdę zmusza mnie do drapania się po głowie i poważnego zastanawiania się nad wycofaniem się do domowego rozwiązania TransactionScope, opartego na obiektach transakcyjnych ADO.NET. To pozornie szalony - ten sam kod, który działa (i nie eskalować) na pół naszego dewelopera robi eskalacji drugiej dewelopera.
Miałem nadzieję na lepszą odpowiedź dla Trace, dlaczego transakcja została eskalowana do DTC, ale niestety tak nie jest.
Oto przykładowy fragment kodu, który spowoduje problemy, na komputerach, które próbują eskalować, próbuje eskalować przy drugim połączeniu. Open () (i tak, w tym momencie nie jest otwarte żadne inne połączenie).
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ę wkopaliśmy się 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
SQL2005SQL2008
Programiści, na których nie działa:
- Dev 4: Windows 7 x64,
SQL2008SQL2005 - Dev 5: Windows Vista x86, SQL2005
- Dev 6: Windows XP X86, SQL2005
- My Home PC: Windows Vista Home Premium, x86, SQL2005
Powinienem dodać, że wszystkie maszyny, starając się znaleźć problem, zostały w pełni załatane wszystkim, co jest dostępne z 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 przykładowy kod, wyraźnie pokazuje połączenie zagnieżdżone z drugim (do drugiego serwera SQL, faktycznie), który przejdzie 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 zagnieżdżamy otwierania połączeń wtórnych - nie powinno dojść do 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 z SQL2000. Używamy SQL2005 / 2008
- http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN na temat eskalacji transakcji.
Ta strona eskalacji transakcji MSDN stwierdza, że następujące warunki spowodują eskalację transakcji do DTC:
- W transakcji zarejestrowany jest co najmniej jeden trwały zasób, który nie obsługuje powiadomień jednofazowych.
- Transakcja obejmuje co najmniej dwa trwałe zasoby, które obsługują powiadomienia jednofazowe. Na przykład rejestracja pojedynczego połączenia nie powoduje promowania transakcji. Jednak za każdym razem, gdy otworzysz drugie połączenie z bazą danych, które powoduje zarejestrowanie bazy danych, infrastruktura System.Transactions wykrywa, że jest to drugi trwały zasób w transakcji, i przekształca go w transakcję MSDTC.
- 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 zestawiany według wartości, 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, wywołując metodę zdalną, która przyjmuje jako parametr Transakcję lub możesz spróbować uzyskać dostęp do zdalnego komponentu obsługiwanego przez transakcję. Powoduje to serializację obiektu transakcji i skutkuje eskalacją, tak jak w przypadku serializacji transakcji w domenie aplikacji. Jest on 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 z jednym „trwałym zasobem”. Czy jest jakikolwiek sposób, żeby mogło się zdarzyć # 1? Jakaś konfiguracja SQL2005 / 8, która powoduje, że nie obsługuje powiadomień jednofazowych?
Aktualizacja 2:
Osobiście ponownie zbadaliśmy wszystkie wersje SQL Server - „Dev 3” faktycznie ma SQL2008, a „Dev 4” to właściwie SQL2005. To nauczy mnie, że nigdy więcej nie zaufam moim współpracownikom. ;) Z powodu tej zmiany danych jestem pewien, że znaleźliśmy nasz problem. Nasi programiści SQL2008 nie mieli problemu, ponieważ SQL2008 zawiera wiele niesamowitych elementów, których nie ma SQL2005.
Mówi mi również, że ponieważ będziemy obsługiwać SQL2005, że nie możemy używać TransactionScope tak jak my, a jeśli chcemy użyć TransactionScope, będziemy musieli przekazać pojedynczy obiekt SqlConnection ... co wydaje się problematyczne w sytuacjach, w których SqlConnection nie można łatwo ominąć ... po prostu pachnie globalną instancją SqlConnection. Ławka w kościele!
Aktualizacja 3
Aby wyjaśnić tutaj w pytaniu:
SQL2008:
- Umożliwia wiele połączeń w ramach jednego TransactionScope (jak pokazano w powyższym przykładowym kodzie).
- Zastrzeżenie # 1: Jeśli zagnieżdżonych zostanie wiele SqlConnection, to znaczy, że dwa lub więcej SqlConnection zostanie otwartych w tym samym czasie, TransactionScope natychmiast eskaluje do DTC.
- Zastrzeżenie # 2: Jeśli dodatkowe SqlConnection zostanie otwarte dla innego „trwałego zasobu” (tj. Innego SQL Servera), natychmiast przejdzie do DTC
SQL2005:
- Nie zezwala na wiele połączeń w ramach jednego TransactionScope, kropka. Nastąpi eskalacja, gdy / jeśli zostanie otwarte drugie połączenie SqlConnection.
Aktualizacja 4
W interesie czyni to pytanie jeszcze bałagan użyteczne, a tylko przez wzgląd na większą przejrzystość, oto jak można dostać SQL2005 eskalować 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ę to po prostu zepsute, ale chyba rozumiem, czy każde połączenie SqlConnection.Open()
jest pobierane z puli połączeń.
„Dlaczego jednak tak się dzieje?” Cóż, jeśli użyjesz SqlTableAdapter przed tym połączeniem przed jego otwarciem, SqlTableAdapter otworzy i zamknie połączenie, skutecznie kończąc transakcję za ciebie, ponieważ teraz nie możesz go ponownie otworzyć.
Zasadniczo więc, aby z powodzeniem korzystać z TransactionScope z SQL2005, potrzebujesz jakiegoś globalnego obiektu połączenia, który pozostaje otwarty od momentu pierwszego TransactionScope jest tworzony, dopóki nie będzie już potrzebny. Oprócz zapachu kodu globalnego obiektu połączenia, otwieranie połączenia jako pierwsze i zamykanie na końcu jest sprzeczne z logiką otwierania połączenia tak późno, jak to możliwe i zamykania go tak szybko, jak to możliwe.
źródło
Odpowiedzi:
SQL Server 2008 może używać wielu
SQLConnection
s w jednymTransactionScope
bez eskalacji, pod warunkiem, że połączenia nie są otwarte w tym samym czasie, co spowodowałoby wiele „fizycznych” połączeń TCP, a zatem 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 poprawnie określiłeś, którzy eskalują, a którzy nie?
Najbardziej oczywistym wytłumaczeniem byłoby to, że programiści z SQL Server 2008 nie rozwijają się.
źródło
TransactionScope
może się przydać z pojedynczymCOMMIT
po stronie serwera, co uczyniłoby eskalację zbędną.Wynik moich badań na ten temat:
Zobacz Unikanie niepożądanych eskalacji do transakcji rozproszonych
Nadal badam zachowanie eskalacji Oracle: Czy transakcje obejmujące wiele połączeń z tym samym DB eskalują się do DTC?
źródło
Że kod będzie powodować eskalację po podłączeniu do 2005 roku.
Sprawdź dokumentację MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx
Nie mogę wyjaśnić, dlaczego Dev 3: Windows 7 x64 odnosi sukcesy, a Dev 4: Windows 7 x64 kończy się niepowodzeniem. Czy na pewno nie jest odwrotnie?
źródło
Nie wiem, dlaczego ta odpowiedź została usunięta, ale wydaje się, że zawiera pewne istotne informacje.
odpowiedział 4 sierpnia 2010 o 17:42 Eduardo
Ustaw Enlist = false w parametrze połączenia, aby uniknąć automatycznego rejestrowania przy transakcji.
Ręcznie rejestruj połączenie jako uczestników zakresu transakcji. [ oryginalny artykuł jest nieaktualny] lub zrób to: Jak zapobiec automatycznej promocji MSDTC [archive.is]
źródło
Nie jestem pewien, czy problemem jest połączenie zagnieżdżone. Dzwonię do lokalnej instancji serwera SQL i nie generuje DTC?
źródło
TransactionScope zawsze przechodzi do transakcji DTC, jeśli korzystasz z dostępu więcej niż 1 połączenie wewnątrz. Jedyny sposób, w jaki powyższy kod może działać z wyłączonym kodem DTC, to jeśli przez ogromne prawdopodobieństwo uzyskasz to samo połączenie z puli połączeń za każdym razem.
„Problem polega na tym, że na połowie maszyn naszych programistów możemy pracować z wyłączonym MSDTC”. Czy na pewno jest wyłączony;)
źródło
Upewnij się, że parametr connectionString nie ustawia dla puli wartości false. Spowoduje to nowe połączenie dla każdego nowego SqlConnection w TransactionScope i eskaluje je do DTC.
źródło