ADO.NET Konflikty i kłopoty Tomasz Kopacz Microsoft Professional Developer Days 2004
Konflikty Na poziomie „interfejsu użytkownika” Dwie osoby zmieniają ten sam wiersz Rozwiązanie: Odpowiedni DataAdapter Mechanizm „blokad aplikacyjnych” na poziomie SQL Server Na poziomie mechanizmów bazy Konflikt równolegle wydawanych poleceń Stracone aktualizacje, odczyty widmo itp Transakcje bazodanowe EnterpriseServices
Architektura ADO (dokładniej – SQLClient) DataSet Własne mechanizmy Provider danych dla Sql Server… SqlDataAdapter CommandBuilder IDataReader Sql Server SqlConnection SqlCommand SqlDataParameter SqlException SqlError SqlTransaction
DataAdapter SelectCommand, InsertCommand, UpdateCommand, DeleteCommand Można w transakcji CommandBuilder Kosztowne! (projektant, dodatek itp.) Wsparcie w DataSet DataRowVersion – wersja wiersza Original, Current, Proposed, Default Podstawienie z kolekcji Parameters SqlCommand SourceVersion DataRowState – stan wiersza Added,Deleted,Modified,Unchanged Detached
Strategie rozwiązywania konfliktów Optymistyczna! Porównujemy tylko klucz główny Zakładamy zmiany w każdym polu Standardowa Wszystkie pola Timestamp Pole charakterystyczne? Różnice w wydajności zależą od łącza do SQL, wielkości danych itp. 100 MB – ok. 19% timestamp szybszy VPN 128 kb – nawet 60% Pesymistyczna Blokada w transakcji od czasu odczytu
Timestamp i wykrywanie konfliktów Długie dane TS xxxxxxxxxx aa OK! xxxxxxxxxx aa yyyyyy bb Zapis yyyyyy bb Konflikt xxxxxxxxxx aa zzzzzz cc Czas Klient A Klient B
Typy aktualizacji „Wszystkie albo żaden” Tylko wiersze bez konfliktów Trzeba aktualizację zamknąć w transakcji Tylko wiersze bez konfliktów ContinueUpdateOnError=true (Sprawdzamy: HasErrors/GetErrors) Pesymistyczne = blokada na tabeli Wystartować transakcję przed wypełnieniem DataSet (I zastanowić się czy nie da się postąpić inaczej)
Transakcje bazodanowe Zapewniają ACID Obiekt SqlTransaction „Pobierany” z połączenia Przypisywana do polecenia Tryby izolacji Podpowiedzi przy poleceniach SQL NOLOCK, READCOMMITTED,REPEATABLEREAD, SERIALIZABLE TABLOCK (jak ktoś naprawdę musi!) Temat w pomocy: Locking Hints
Tryb izolacji a problemy współbieżności Poziom izolacji (wg „kosztowności”) ReadUncommitted ReadCommited RepeatableRead Serializable Problem z współbieżnością Stracone aktualizacje X Odczyt właśnie zmienianych rekordów ? OK Niespójne odczyty Odczyty rekordów „widmo”
Demo – praca z wersjami w DataSet Blokady optymistyczne Aktualizacje wszystkich które można Aktualizacje tylko wszystkich lub żadnego Pokazanie użytkownikowi zmienionych danych Wersje DataSet i okolice Blokady pesymistyczne
Własne transakcje Problem: Rozwiązanie: Okno „tylko dla jednego użytkownika” Rozwiązanie: Transakcje aplikacyjne w SQL Server sp_getapplock nazwa_zasobu, tryb, właściciel, timeout sp_releaseapplock nazwa_zasobu, właściciel Można używać Wewnątrz sesji (jednego połączenia) Nie lokalne – trzeba użyć np. mutex-a Wewnątrz transakcji
Demo - własne transakcje aplikacyjne Blokada okna Trwa aż się zwolni zasób Lub – sesja zostanie zamknięta
Enterprise Services? Problem: Rozwiązanie: Transakcja obejmuje kilka etapów Połączeń, sekwencja operacji bazodanowych itp Złożona logika i wygodniej byłoby dodać cechę „wykonuj w transakcji” Kod złożony i zmiany trzeba wycofywać „ręcznie” Rozwiązanie: Obiekt dziedziczący z EnterpriseServices Duże możliwości (kolejki, pula itp.) Transakcje Połączenie „wylistowane” w transakcji Kod „kompensacyjny” Rejestracja regsvcs.exe System.EnterpriseServices.RegistrationHelper.InsallAssembly
Serwerowy obiekt ServicedComponent Referencja do System.EnterpriseServices Klasa dziedziczy z ServicedComponent [assembly: ApplicationActivation(ActivationOption.Server/Library)] Atrybuty klasy Transaction Disabled NotSupported Required RequiresNew Supported Atrybuty metody: [ AutoComplete(true) ] Lub: ContextUtil.SetAbort()/.SetComplete();
Własny kod kompensujący Problem: Używany obiekt nie jest „świadomy transakcji” Długa operacja (szkoda zasobów SQL-a) Rozwiązanie: Kod kompensujący System.EnterpriseServices.CompensatingResourceManager Główne klasy Clerk Compensator LogRecord [assembly: ApplicationCrmEnabled] ActivationOption.Server
Demo - EnterpriseServices Transakcje automatyczne Transakcje ręczne Kod kompensacyjny Plik + kasowanie Uwagi! Interfejs Raz skompilować oddzielny DLL – ułatwi pracę Dodać do GAC Łatwiej znajdzie formatter itp Referencje do DLL a nie do projektu
Zdalnie? Problem: Rozwiązanie: Typy wywołań: Klient: Obiekt EnterpriseServices na zdalnej maszynie Rozwiązanie: Remoting Własny proces nasłuchujący (remoting) + wrapper Umieścić w /bin aplikacji WWW + drobne zmiany w web.config/remoting.config Uwaga! ASPNET nie zarejestruje samo COM+ System.UnauthorizedAccessException: Odmowa dostępu do klucza rejestru HKEY_CLASSES_ROOT\EnterpriseDemoSrv.EntComp. Typy wywołań: SAO (SingleCall/Singleton) CAO (Jak „normalne” COM/.NET) Klient: Activator.GetObject(typeof(…),"uri");
Szybkie demo – zdalne wywołanie Pliki dll kopiowane do bin Zmiany w web.config Prawa dla anonimowego użytkownika! Klient – interfejs + Activator.GetObject
Warto jeszcze wiedzieć SWC (Services Without Components) COM+ 1.5 (XP, 2003 Server) BYOT (Bring Your Own Transaction) CreateWithTransaction, CreateWithTipTransaction Windows 2003 – domyślnie COM+ i DTC zablokowane Add/Remove Windows Components Application Server Enable network COM+ access Enable network DTC access Component Services – (…) - Security Configuration Prościej używać logowanie SQL Jeżeli coś nie działa: Uprawnienia… Usunąć cały komponent (okno Component Services) W Event Log -> co nie działa
Cechy omawianych mechanizmów… Konflikty Timestamp – najtańszy; Porównywanie pól – gdy nie można zmienić schematu Inne charakterystyczne pole DataSet ma olbrzymie możliwości… Blokady aplikacyjne w SQL Co jak klient padnie? master.syslockinfo/master.sysprocesses kodowane @@SPID – kod połaczenia; do oddzielnej tabeli Ale i tak – blokady sesyjne zwalniane są w momencie zakończenia (padu) sesji Klient/Serwer – transakcje lokalne Szybkie, ale… „ręczne” EnterpriseServices Gdy mamy taką możliwość Nawet „na kliencie” Wolniejsze Proste w użyciu - wystarczy deklaracja atrybutu Łatwa migracja na „n warstw”
Co zmieni Yukon Kursory po stronie serwera SqlDependency Ale nadal – nie używać SqlDependency Jedno połączenie – wiele równoległych poleceń Mniejsza rola transakcji w EnterpriseServices? Indigo jest ciekawym produktem I zawiera funkcjonalność EnterpriseServices
Microsoft Professional Developer Days 2004 Dziękuję za uwagę Pytania: tkopacz@tomaszkopacz.com Prezentacja + przykłady do ściągnięcia dostępne na http://www.microsoft.com/poland/seminaria/prezentacje.asp Microsoft Professional Developer Days 2004