Rozdział 20.
Testowanie stron ASP.NET
W tym rozdziale zostanie poruszone niezwykle ważne zagadnienie — testowanie stron ASP.NET. Wszyscy popełniamy błędy, niezależnie od tego jak doskonałymi programistami byśmy nie byli. Dlatego też konieczność testowania aplikacji jest oczywistym faktem. Im tworzony kod jest dłuższy, tym więcej w nim będzie błędów.
Jest całkiem prawdopodobne, że spotkałeś się już z wieloma błędami w programach komputerowych. Przypomnij sobie te wszystkie komunikaty generowane przez tradycyjne aplikacje bądź witryny WWW, opisujące błędy których zupełnie nie rozumiałeś. W większości przypadków błędy te oznaczały przerwanie pracy programu, lecz czasami zdarzało się także, iż powodowały one generowanie nieodpowiednich danych wynikowych. Niemniej jednak, w każdym z tych przypadków nie byłeś w stanie zrobić tego co zaplanowałeś. W idealnym przypadku powinniśmy napisać kod i zapomnieć o nim, jednak bardzo często jest to zupełnie nierealne.
ASP.NET udostępnia dwa wspaniałe narzędzia służące do testowania aplikacji — program uruchomieniowy CLR (ang.: Microsoft Common Language Runtime Debugger) oraz usługę śledzenia (ang.: Trace Service). Oba te narzędzia zostaną szczegółowo omówione w treści niniejszego rozdziału, podobnie jak instrukcje try i catch.
W tym rozdziale zostaną omówione następujące zagadnienia:
Sposoby zastosowania instrukcji try oraz catch do testowania aplikacji.
Testowanie aplikacji przy wykorzystaniu metody Response.Write.
Sposoby tworzenia i zgłaszania własnych błędów.
Czym jest narzędzie śledzenia oraz jak można z niego korzystać.
W jaki sposób można wykorzystać program uruchomieniowy CLR do testowania aplikacji podczas jej działania.
Informacje wstępne dotyczące testowania aplikacji
Prawdopodobnie zdarzyło Ci się już popełnić kilka błędów podczas tworzenia stron ASP.NET. Mogły to być proste błędy polegające na pominięciu atrybutu runat="server", lub całkiem złożone, których odnalezienie wymagało długich dni żmudnej pracy. Błędy są bardzo frustrujące, a ciągłe oglądanie stron zawierających komunikaty o nich (takich jak ta przedstawiona na rysunku 20.1) może sprawić, że będziesz chciał wyrywać sobie włosy z głowy.
Rysunek 20.1. Typowa strona z informacjami o błędach, generowana przez ASP.NET.
Wraz ze wzrostem stopnia złożoności tworzonych aplikacji, coraz trudniej jest zapobiegać powstawaniu nowych błędów oraz odnajdywać i poprawiać błędy, które już zostały zrobione. Przyjrzymy się typowemu przykładowi przedstawionemu na listingu 20.1.
Listing 20.1. Odnajdywanie błędów.
<%@ Page Language="VB %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDB" %>
<script runat="server">
sub Page_Load(obj as Object, e as EventArgs)
'tworzymy połączenie
dim objConn as new OleDbConnection _
("Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\ASPNET\data\banking.mdb")
'otwieramy połączenie
dim objCmd as new OleDbDataAdapter _
("select * from tblUsers", objConn)
'pobieramy dane
dim ds as DataSet = new DataSet()
objComm.Fill(ds, "tblUsers")
end sub
</script>
<html><body>
<form runat="server">
</form>
</body></html>
Spróbuj wyświetlić tę stronę w przeglądarce (nie zapomnij wcześniej włączyć testowania w pliku konfiguracyjnym web.config). W przeglądarce pojawi się strona przypominające tą z rysunku 20.2.
Rysunek 20.2. Ojej, coś złego stało się z kodem naszej strony
Jest oczywiste, że kod przedstawiony na listingu 20.1 nie jest w porządku; a jeśli jeszcze nie zauważyłeś gdzie znajduje się błąd, to informacje wyświetlone na stronie z rysunku 20.2 pomogą Ci go odnaleźć. Warto zwrócić uwagę, iż na stronie z rysunku 20.2 został wyróżniony wiersz kodu źródłowego, w którym wystąpił błąd, a dodatkowo zostały na niej wyświetlone informacje o numerze tego wiersza oraz nazwie pliku. Wygląda na to, że w wierszu 18 popełniliśmy prosty błąd typograficzny. Zamiast nazwy objCmd została użyta nazwa objComm. Dzięki szczegółowym informacjom wyświetlanym na stronie, błąd tego typu można bardzo łatwo poprawić. Po wprowadzeniu stosownych zmian, nasz kod będzie działać bez zarzutu.
Przyjrzyjmy się dokładniej stronie z rysunku 20.2. Łatwo zauważyć, iż zawiera ona całkiem sporo dodatkowych informacji o przyczynie błędu. Na przykład, u dołu tej strony znajdują się dwa połączenia — Show Detailed Compiler Output (pokaż szczegółowe wyniki wygenerowane przez kompilator) oraz Show Complete Compilation Source (pokaż kompletny kod źródłowy użyty podczas kompilacji strony). Kliknięcie tych połączeń spowoduje wyświetlenie na stronie nowych informacji, przedstawionych na rysunku 20.3.
Rysunek 20.3. Bardziej szczegółowe informacje o błędzie
-->
Notatka
Jeśli nie został włączony tryb testowania, to dwa opisywane połączenia do dodatkowych informacji na temat błędu, mogą być niedostępne. Więcej informacji na ten temat znajdziesz w rozdziale 18., pt.: „Konfiguracja i wdrażanie aplikacji ASP.NET”.[Author:p8R]
Fragment strony wyświetlany po kliknięciu pierwszego z tych połączeń — Show Detailed Compiler Output — zawiera informacje o tym, co mechanizmy ASP.NET próbowały wykonać i dlaczego im się to nie udało. Jego pierwszy wiersz zawiera polecenie, które zostało wydane w celu skompilowania kodu źródłowego strony ASP.NET. (Jak widać został do tego celu użyty kompilator języka Visual Basic.NET.) Po sprawdzeniu parametrów wywołania kompilatora oraz wykorzystanych w tym poleceniu obiektów biznesowych, wszystkie pozostałe elementy polecenia powinne być znajome.
Poniżej polecenia, zostały wyświetlone wyniki wygenerowane przez kompilator, które mogą nam pomóc przy określaniu przyczyny błędu. Ten komunikat jest widoczny także na rysunku 20.2. Fragment strony wyświetlany po kliknięciu drugiego połączenia — Show Complete Compilation Source — prezentuje pełny kod źródłowy pliku, który miał być skompilowany, w tym także wszelkie polecenia kompilatora oraz funkcje maszyny wirtualnej CLR.
Ogólnie rzecz biorąc, udostępnianie tych informacji użytkownikom końcowym nie jest zalecane. Przed wszystkim, większość użytkowników w ogóle nie chce oglądać jakichkolwiek komunikatów o błędach. A co gorsze, sprytny użytkownik może wykorzystać ten kod przeciwko nam. A zatem, jest niezwykle ważne, aby przetestować strony przy użyciu wszelkich dostępnych środków i narzędzi, i zrobić wszystko co możliwe, aby takie komunikaty o błędach się nie pojawiały.
Na szczęście ASP.NET udostępnia kilka mechanizmów, które pomagają przy testowaniu aplikacji i obsłudze błędów. Pierwszy z nich został przedstawiony już wcześniej — są nim instrukcje try oraz catch umieszczane w różnych miejscach programów (na przykład, w listingu 19.1, przedstawionym w poprzednim rozdziale). Instrukcje te pozwalają na przechwytywanie potencjalnych błędów, które mogą wystąpić w aplikacji. W podobny sposób może nam pomóc narzędzie śledzące, które oprócz tego udostępnia dodatkowe informacje. I w końcu ostatnim narzędziem które można wykorzystać podczas testowania aplikacji, jest program uruchomieniowy CLR. Jest to potężne narzędzie służące do odnajdywania i poprawiania błędów, które się już pojawiły.
Instrukcje try i catch
Zazwyczaj, gdy jakiś fragment kodu spowoduje błąd, to wykonywanie aplikacji zostaje przerwane, a w przeglądarce jest wyświetlany stosowny komunikat o błędzie (przykładowa postać takiego komunikatu została przedstawiona na rysunku 20.1). Jeśli istnieje jakakolwiek możliwość uniknięcia wyświetlenia tego komunikatu, to należy ją wykorzystać (w końcu, w idealnym przypadku, błędy nigdy nie powinne byś wyświetlane).
Użycie instrukcji try informuje ASP.NET że pewien blok kodu należy wykonać „próbnie”. W takim przypadku, jeśli jakikolwiek fragment kodu zapisany wewnątrz tej instrukcji spowoduje zgłoszenie błędu, to nie doprowadzi on do przerwania wykonywania aplikacji i wyświetlenia stosownego komunikatu, lecz będziemy mieli szansę rozwiązania zaistniałego problemu. Na przykład, gdyby wiersz 18. listingu 20.1 został zapisany wewnątrz instrukcji try, to istniałaby potencjalna szansa uniknięcia zatrzymania aplikacji przez zgłaszany błąd i jej dalszego wykonywania.
Instrukcja try służy do ochrony fragmentów kodu, które mogą spowodować powstanie błędów. W przypadku tworzenia tradycyjnych aplikacji stosowanie tych instrukcji jest koniecznością, gdyż wykonanie niektórych operacji na pewno spowoduje zgłoszenie błędu. Być może o tym nie wiesz, ale kliknięcie przycisku Anuluj w oknie dialogowym zazwyczaj powoduje zgłoszenie błędu, który aplikacja w jakiś sposób musi obsłużyć. Instrukcja try oraz inne instrukcje używane wraz z nią, są wspólnie określane mianem strukturalnej obsługi wyjątków.
Analizując przykłady podane w niniejszej książce można się już było przekonać, że składnia instrukcji try jest całkiem prosta. Składa się ona z instrukcji try, po którym jest umieszczany chroniony blok kodu, po którym są z kolei umieszczane instrukcje catch oraz finally. Oto przykład zapisu tej instrukcji:
'tworzymy obiekt OleDbCommand
try
objCmd.Connection.Open
'....
catch
obsługa błędu
end try
W tym przypadku chroniony jest kod zapisany pomiędzy wierszami 2. i 5., gdyż to właśnie on może spowodować zgłoszenie błędu. Jeśli wewnątrz bloku try nie został zgłoszony żaden błąd, to wykonywanie programu zostaje przeniesione do wiersza 7., a pozostały kod jest realizowany w standardowy sposób. Jeśli jednak błąd zostanie zgłoszony, to wykonywanie programu zostanie przeniesione do instrukcji catch zapisanej w wierszu 5. Wewnątrz tej instrukcji umieszczany jest kod służący do obsługi błędu.
Nowe wyrażenie
Przyjrzyjmy się bliżej technicznym szczegółom całego procesu. Gdy w aplikacji VB.NET pojawia się błąd to mówimy że został zgłoszony wyjątek. Oznacza to, że stało się coś co nie powinno się stać i VB.NET „rzuca w powietrze” błąd, aby ktoś inny go złapał i obsłużył. Jeśli wyjątek nie zostanie w żaden sposób obsłużony, to błąd spowoduje przerwanie realizacji aplikacji a informacje o błędzie zobaczy użytkownik końcowy.
Zgłaszanie wyjątków pozwala na przechwytywanie błędów zanim doprowadzą one do przerwania wykonywania aplikacji i wyświetlenia stosownego komunikatu. Jeśli błąd w porę zostanie przechwycony, to istnieje możliwość zapobieżenia awarii i rozwiązania zaistniałego problemu.
Spróbujmy zatem użyć instrukcji try. Gdy pojawi się błąd instrukcja ta zaalarmuje towarzyszącą jej instrukcję catch, która przechwyci wyjątek i obsłuży go. Listing 20.2 przedstawia przykład zastosowania tych instrukcji.
--> Listing 20.2. Przykład testowania strony ASP.NET.[Author:p8R]
<%@ Page Language="VB" Debug="true" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<script runat="server">
dim Conn as new OleDbConnection("Provider=" & _
"Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\ASPNET\data\banking.mdb")
sub GetData(obj as Object, e as EventArgs)
dim objCmd as OleDbCommand = new OleDbCommand _
("select * from tblUsers where UserID = @ID", Conn)
dim objReader as OleDbDataReader
dim objParam as OleDbParameter
objParam = objCmd.Parameters.Add("@ID", OleDbType.Integer)
objParam.Direction = ParameterDirection.Input
objParam.Value = tbID.Text
objCmd.Connection.Open()
objReader = objCmd.ExecuteReader
dgData.DataSource = objReader
dgData.DataBind()
objReader.Close
objCmd.Connection.Close()
end sub
</script>
<html><body>
<form runat="server">
<asp:Label id="lblMessage" runat="server"
maintainstate=false /><br>
Podaj identyfikator: <asp:TextBox id="tbID" runat="server"
AutoPostBack=True
OnTextChanged=GetData /><p>
<asp:DataGrid id="dgData" runat="server"
BorderColor="black"
GridLines="Vertical"
width="100%"
Font-Name="Arial"
Font-Size="8pt"
HeaderStyle-BackColor="#cccc99"
ItemStyle-BackColor="#ffffff"
AlternatingItemStyle-Backcolor="#cccccc"
AutoGenerateColumns="true" />
</form>
</body></html>
Analiza
Powyższy listing przedstawia kod bardzo prostej strony ASP.NET wyświetlającej informacje o użytkowniku na podstawie podanego identyfikatora. W momencie podania identyfikatora użytkownika w polu tekstowym, wykonywane są polecenia SQL, które pobierają dane z bazy i wiążą je z elementem sterującym DataGrid. Co się jednak stanie jeśli w polu tekstowym przypadkowo zostanie wpisana jakaś litera? Wyniki wygenerowane przez stronę w takim przypadku przedstawione zostały na rysunku 20.4.
Rysunek 20.4. Wpisanie nieprawidłowych danych w formularzu powoduje błąd
Użytkownicy absolutnie nie powinni oglądać takich stron. Dodajmy zatem do kodu strony z listingu 20.2 instrukcję try, dzięki której będziemy w stanie obsłużyć błąd. Wiersze do 20. do 27. listingu 20.2 zastąp kodem przedstawionym na listingu 20.3.
Listing 20.3. Wykorzystanie instrukcji try w przykładzie z listingu 20.2.
try
objCmd.Connection.Open()
objReader = objCmd.ExecuteReader
dgData.DataSource = objReader
dgData.DataBind()
objReader.Close
objCmd.Connection.Close()
catch
lblMessage.Text = "Nieprawidłowe dane wejściowe!"
end try
Spróbuj ponownie wyświetlić tę stronę; teraz wygenerowane wyniki powinne przypominać te z rysunku 20.5.
Rysunek 20.5. Wyniki wykorzystania mechanizmów obsług błędów
Instrukcja try przechwyciła błąd zanim spowodował on jakiekolwiek problemy i wyświetliła stosowny komunikat (patrz wiersz 30. listingu 20.3). Następnie wykonywanie programu zostało wznowione w wierszu 31. i zakończone w normalny sposób.
Wyjątki są pogrupowane hierarchicznie. Klasą bazową wszystkich wyjątków, jest klasa System.Exception. Bezpośrednio poniżej niej została umieszczona klasa SystemException, a z kolei poniżej tej klasy — wszystkie inne wyjątki, takie jak na przykład OleDbException czy też FormatException.
Schodząc ku dołowi hierarchii, błędy reprezentowane przez należące do niej klasy stają się coraz bardziej szczegółowe. Na przykład, klasa SystemException obejmuje wszystkie wbudowane błędy, natomiast klasa FormatException wyłącznie błędy spowodowane podaniem nieprawidłowych informacji. Wszystkie te wyjątki są klasami .NET, podobnie zresztą jak wszystkie inne obiekty z którymi spotykaliśmy się w tej książce. Fragment drzewa hierarchii klas wyjątków został przedstawiony na rysunku 20.6.
Rysunek 20.6. Fragment drzewa hierarchii klas wyjątków
Opis rysunku
Wszystko tak jak jest w oryginale, to są bowiem nazwy klas, których nie należy tłumaczyć.
Notatka
Zwróć uwagę, iż choć klasa SystemException znajduje się w hierarchii poniżej klasy Exception, to należy ona do przestrzeni nazw System. Oznacza to, że należy się do niej odwoływać za pomocą nazwy System.SystemException, a nie System.Exception.SystemException.
Wykorzystanie ogólnej postaci instrukcji catch użytej w poprzednim przykładzie, umożliwia przechwytywanie błędów wszelkich możliwych typów. Innymi słowy instrukcja taka powoduje przechwytywanie wszystkich wyjątków, których klasy leżą w drzewie hierarchii poniżej klasy Exception. Istnieje jednak możliwość przechwytywania wyłącznie wybranych typów wyjątków; w tym celu wykorzystywana jest instrukcja catch o następującej postaci:
catch nazwaZmiennej as TypWyjątku
Na przykład, fragment kodu przedstawionego na listingu 20.3 można by zamienić kodem z listingu 20.4.
Listing 20.4. Zmodyfikowany blok try.
try
objCmd.Connection.Open()
objReader = objCmd.ExecuteReader
dgData.DataSource = objReader
dgData.DataBind()
objReader.Close
objCmd.Connection.Close()
catch objEx as FormatException
lblMessage.Text = objEx.Message
catch objEx as OleDbException
lblMessage.Text = "Błąd bazy danych!"
catch objEx as Exception
lblMessage.Text = "Nieznany błąd!"
end try
W wierszu 29. przechwytywane są wyjątki typu FormatException (jak również wszystkie wyjątki klas potomnych klasy FormatException). Jak na razie nie zwracaj uwagi na właściwość Message wykorzystaną w wierszu 30. — zostanie ona opisana w dalszej części rozdziału. W wierszu 31. przechwytywane są wyjątki OleDbException, natomiast w wierszu 33. — wszelkie pozostałe wyjątki. Jak wiadomo wyjątki są hierarchiczne, a zatem instrukcja catch będzie przechwytywać i obsługiwać wyjątki deklarowanej klasy jak i wszystkie wyjątki jej klas potomnych (czyli bardziej szczegółowe). Dlatego poszczególne instrukcje catch należy zapisywać w ściśle określonej kolejności, umieszczając najbardziej szczegółowe wyjątki na początku, a te najbardziej ogólne — na końcu. W razie wykorzystania instrukcji catch należy zwrócić uwagę iż:
ze względu na możliwość zgłoszenia wyjątków konkretnych klas potomnych (takich jak FormatException lub OleDbException) lub konieczność wykonania szczególnych czynności podczas obsługi tych wyjątków, należy je traktować jako osobne przypadki i przechwytywać niezależnie od pozostałych;
konstrukcja try … catch przerywa przetwarzania wyjątków po odnalezieniu instrukcji catch służącej do obsługi wyjątków danego typu;
Typ Exception jest najbardziej ogólnym typem wyjątków i użycie instrukcji catch xx as Exception przypomina nieco użycie bloku case else w instrukcji if.
Zastąpienie wierszy od 20. do 27. z listingu 20.2 fragmentem kodu przedstawionym na listingu 20.4 spowoduje wygenerowanie strony wynikowej pokazanej na rysunku 20.7.
Rysunek 20.7. Wykorzystanie kilku instrukcji catch umożliwia przechwytywanie bardziej szczegółowych błędów. Tekst opisujący dany błąd można pobrać przy użyciu właściwości Message obiektu wyjątku
Jest jeszcze jedna instrukcja, której można używać wraz z instrukcją try — finally. Umożliwia ona podanie kodu wykonującego operacje porządkowe lub zakańczające przetwarzanie, które zostaną wykonane niezależnie od tego, czy został zgłoszony jakiś wyjątek czy nie. Wiersze od 20. do 27. listingu 20.2 można by zastąpić fragmentem kodu przedstawionym na listingu 20.5, dzięki czemu połączenie z bazą danych było by zamykane zawsze, niezależnie od tego czy wystąpiły jakieś błędy czy nie.
Listing 20.5. Wykorzystanie instrukcji finally.
try
objCmd.Connection.Open()
objReader = objCmd.ExecuteReader
dgData.DataSource = objReader
dgData.DataBind()
objReader.Close
catch objEx as FormatException
lblMessage.Text = objEx.Message
catch objEx as OleDbException
lblMessage.Text = "Błąd bazy danych!"
catch objEx as Exception
lblMessage.Text = "Nieznany błąd!"
finally
objCmd.Connection.Close()
end try
Cały proces wykorzystania instrukcji try został przedstawiony na rysunku 20.8.
Rysunek 20.8. Przebieg realizacji programu wykorzystującego instrukcję try
Opis rysunku
Execute code … — Wykonaj bloku kodu zapisany wewnątrz instrukcji try
Error? — Błąd?
Yes — Tak
No — Nie
Execute catch… — Wykonaj blok kodu zapisany wewnątrz instrukcji catch
Exscute finally … — Wykonaj blok kodu zapisany wewnątrz instrukcji finally
Continue … — Kontynuuj dalsze wykonywanie programu
Każda klasa wyjątku udostępnia właściwości, które można wykorzystać do określenia przyczyny błędu:
HelpLink — połączenie z plikiem zawierającym bardziej szczegółowe informacje na temat błędu (zarówno ten plik jak i połączenie do niego należy stworzyć samemu).
InnerException — odwołanie do wewnętrznego wyjątku. Jeśli inny wyjątek został przechwycony i przekazany do innej procedury obsługi, to właściwość ta będzie zawierać odwołanie do niego.
Message — komunikat opisujący błąd.
Source — łańcuch znaków zawierający nazwę obiektu, który spowodował powstanie błędu.
StackTrace — właściwość ta zwraca „obraz stosu” zawierający informacje o przyczynie błędu (więcej informacji na ten temat znajdziesz w dalszej części rozdziału, pt.: „Śledzenie”).
TargetSite — metoda, która spowodowała powstanie błędu.
Zgłaszanie wyjątków
Wyjątki są bardzo przydatne i bynajmniej nie służą one wyłącznie do przechwytywania i obsługi błędów. W rzeczywistości mogą zaistnieć sytuacje, w których będziemy chcieli zgłaszać swoje własne wyjątki.
Na przykład, przypomnij sobie serwisy sieci WWW które stworzyliśmy w rozdziałach 16. (pt.: „Tworzenie serwisów sieci WWW”) i 17. (pt.: „Wykorzystanie i zabezpieczanie serwisów sieci WWW”). Załóżmy, że sprawdzamy poprawność informacji na podstawie bazy danych i okazało się, iż nie są one poprawne. W takim przypadku, moglibyśmy zgłosić wyjątek w naszym serwisie, który aplikacja klienta mogłaby przechwycić i obsłużyć przy użyciu instrukcji try i catch.
Do zgłaszania wyjątków służy instrukcja throw:
throw new Exception("O nie! Coś z tym trzeba zrobić!")
Wykonanie powyższej instrukcji spowoduje utworzenie nowego wyjątku bazującego na klasie Exception. Łańcuch znaków umieszczony wewnątrz nawiasów jest komunikatem o błędzie. Gdyby powyższa instrukcja została dodana z listingu 20.2 na samym początku procedury GetData, to wyniki wykonania tej strony przypominałyby te z rysunku 20.9.
Rysunek 20.9. Wykorzystanie instrukcji throw do zgłaszania własnych błędów
Zgłaszanie własnych wyjątków może być bardzo wygodne w sytuacjach gdy sami nie mamy zamiaru ich obsługiwać. O ile tylko funkcja, która wywołała fragment kodu zgłaszający wyjątek jest w stanie go przechwycić i obsłużyć, to nie mamy się czym przejmować.
Na przykład, jedną z sytuacji w których może się przydać możliwość zgłaszania własnych błędów, jest podawanie danych w formularzu. Po przesłaniu danych na serwer, procedura która je obsługuje może wywoływać metodę, która sprawdza czy są one poprawne czy nie (na przykład, czy użytkownik zamiast imienia nie wpisał jakiejś liczby). Metoda ta mogłaby zgłaszać określone przez nas błędy, aby poinformować procedurę, która ją wywołała, że coś jest nie w porządku. Procedura obsługująca dane przesłane z formularza mogłaby następnie odpowiednio obsługiwać te błędy.
Warto wiedzieć, że nasze możliwości nie ograniczają się do zgłaszania własnych wyjątków — można zgłaszać wyjątki wszystkich dostępnych typów. Na przykład, przedstawiona poniżej instrukcja throw zgłasza wyjątek informujący o braku argumentu:
throw new ArgumentNullException()
Możliwości te są niezwykle przydatne w przypadku tworzenia obiektów biznesowych, gdyż pozwalają programistom korzystającym z tych obiektów, na implementację własnych metod obsługi błędów.
Kiedy należy stosować instrukcję try
Instrukcja try jest przydatna podczas testowania kodu, gdyż umożliwia wykrywania błędów bez przerywania realizacji programu. Istnieje kilka podstawowych zasad, do których należy się stosować w razie korzystania z tej instrukcji.
Po pierwsze, instrukcji try zawsze należy używać w sytuacjach gdy są wykorzystywane systemy nie należące do środowiska ASP.NET, takie jak bazy danych lub pliki. W takich przypadkach istniej bowiem bardzo wiele czynników które mogą spowodować powstanie błędów, a nad którymi nie mamy żadnej kontroli (na przykład: brak pliku, niewystarczające prawa dostępu do pliku, brak możliwości nawiązania połączenia z bazą danych, brak tabeli bazy danych na której chce operować polecenie SQL, i tak dalej). I właśnie dlatego, w przypadku wykorzystania zewnętrznych systemów i zasobów, użycie instrukcji try jest absolutną koniecznością.
Instrukcję try warto także stosować w przypadkach gdy nie jesteśmy pewni poprawności napisanego fragmentu kodu, a w szczególności, na etapie testowania i uruchamiania aplikacji. Na przykład, może się zdarzyć że działanie pewnego fragmentu kodu zależy do wielu różnych czynników (doskonałym przykładem jest tu wiązanie danych z elementami sterującymi DataGrid).
Nie należy polegać na instrukcjach try przy wykrywaniu błędów w informacjach podawanych przez użytkownika. Do kontroli poprawności danych należy stosować inne metody, na przykład elementy sterujące Validation, które zostały zaprojektowane specjalnie w tym celu. Elementy te zapewniają większe możliwości programistyczne służące do obsługi tego typu błędów i są w stanie bardziej precyzyjnie określić ich przyczyny niż instrukcje try.
I w końcu ostatnia sprawa, nie należy umieszczać całego kodu wewnątrz instrukcji try. Powinno się je stosować wyłącznie w razie konieczności. Różnego typu koszty związane z wykorzystaniem tych instrukcji mogą być niewielkie, jednak uzyskiwane korzyści nie zawsze są ich warte.
Śledzenie
Być może poznałeś już sposób testowania tworzonych aplikacji przy wykorzystaniu przemyślnie używanych wywołań metody Response.Write. Jeśli podejrzewasz, że jakiś fragment kodu powoduje błędy, to możesz go zakomentować, umieścić w odpowiednim miejscu wywołanie metody Response.Write i przekonać się czy Twe przypuszczenia były słuszne. Na przykład, przeanalizujmy przykład przedstawiony na listingu 20.6.
Listing 20.6. Testowanie fragmentów kodu.
<%@ Page Language="VB" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<script runat="server">
sub Submit(obj as object, e as eventargs)
dim strSQL as string = _
"SELECT UserID FROM tblUsers WHERE " & _
"Firstname = '" & tbUserName.Text & "' AND " & _
"Lastname = '" & tbPassword.Text & "'"
'tutaj wykonujemy inne operacje na bazie danych
end sub
</script>
<html><body>
<form runat="server">
<asp:Label id="lblMessage" runat="server" />
Imię użytkownika:
<asp:Textbox id="tbUserName" runat="server" /><p>
Nazwisko użytkownika:
<asp:Textbox id="tbPassword" runat="server"
TextMode="password" /><p>
<ASP:Button id="tbSubmit" runat="server"
OnClick="Submit"
Text="Prześlij" />
</form>
</body></html>
Analiza
Formularz wyświetlany na powyższej stronie przeszukuje bazę danych na podstawie podanego --> imienia i nazwiska użytkownika[Author:p8R] . Jest to bardzo prosty przykład, lecz wyobraźmy sobie, że z jakichś powodów nie działa on poprawnie i podczas wykonywania strony pojawiają się błędy — być może ich przyczyną są nieodpowiednie informacje zwracane z bazy danych. W podobnych przypadkach, często spotykanym rozwiązaniem jest sprawdzenie poprawności zadawanego zapytania SQL.
Aby sprawdzić poprawność zadawanego zapytania SQL można umieścić następujący fragment kodu, poniżej 10. wiersza listingu 20.6:
Response.Write(strSQL)
Następnie zakomentujmy wszystkie podejrzane wiersze kodu, tak aby uniemożliwić powstawanie błędów. Wyniki wykonania zmodyfikowanej wersji strony (listing2006-v2.aspx) zostały przedstawione na rysunku 20.10.
Rysunek 20.10. Wywołanie metody Response.Write pozwala na sprawdzanie wartości zmiennych
Metoda Response.Write jest bardzo przydatnym narzędziem umożliwiającym śledzenie zmiennych i wyświetlanie ich wartości. Niestety nie jest ona w stanie dostarczyć żadnych innych informacji poza wartością zmiennej, a co gorsze, ciągłe dodawanie i usuwanie jej wywołań w różnych miejscach programu może być prawdziwym utrapieniem.
Mechanizm śledzenia udostępniany przez ASP.NET rozwiązuje ten problem. Pozwala on na wyświetlanie wyrażeń testujących i śledzenie procesu wykonywania kodu. Mechanizm śledzenia informuje jakie metody zostały wykonane, ile czasu zajęło wykonanie każdej z nich, jakie są bieżące wartości zmiennych i udostępnia wiele innych przydatnych informacji, a wszystko to w przejrzystym i czytelnym formacie.
Ważne jest także to, iż po zakończeniu procesu testowania kodu nie trzeba z niego usuwać instrukcji testowych. Wszystkie te instrukcje można wyłączyć przy użyciu jednego polecenia. Mechanizm śledzenia można włączyć na jednej konkretnej stronie lub na wszystkich stronach wchodzących w skład aplikacji. Niezależnie od sposobu użycia, po wyłączeniu instrukcji testujących nie mają one żadnego wypływu na sposób działania aplikacji i nie generują żadnych danych wynikowych, które byłyby przesyłane do klienta.
ASP.NET udostępnia dwa poziomy śledzenia — śledzenie na poziomie strony oraz śledzenie na poziomie aplikacji.
Śledzenie na poziomie strony
O możliwościach mechanizmu śledzenia można się przekonać bardzo łatwo. Wystarczy dodać atrybut Trace="true" do dyrektywy @ Page (w 1. wierszu listingu 20.6):
<%@ Page Language="VB" Trace="true" %>
W powyższy sposób śledzenie na tej stronie zostało włączone. Rysunki 20.11 oraz 20.12 przedstawiają wyniki wygenerowane na stronie po włączeniu śledzenia.
Rysunek 20.11. Mechanizm śledzenia daje dostęp do bardzo wielu informacji przydatnych przy testowaniu stron ASP.NET
Rysunek 20.12. Dalszy fragment tej samej strony ASP.NET
O rany, co się stało!? Nagle na naszej prostej stronie ASP.NET pojawiła cała masa informacji. Przejrzyj wyświetloną stronę i dokładnie przeanalizuj wszystkie jej fragmenty. Jak można się przekonać mechanizm śledzenia udostępnił niemal wszystkie informacje o stronie jakie można sobie wyobrazić! Omówmy poszczególne fragmenty tych informacji.
Na samym początku wciąż jest widoczny formularz z listingu 20.6. Można się nim posługiwać w taki sposób, jak gdyby nie było pod nim żadnych dodatkowych informacji. ASP.NET dodaje te tabele z informacjami automatycznie, w momencie włączenia mechanizmu śledzenia. Zawierają one dane, które mogą się przydać przy testowaniu aplikacji i sprawdzaniu efektywności jej działania. Oczywiście, pod żadnym pozorem informacje te nie powinne być dostępne dla użytkowników końcowych.
Kolejny fragment strony — Request Details (szczegółowe informacje o żądaniu) — zawiera informacje o sposobie w jaki klient zażądał danej strony. Zawiera on unikalny identyfikator sesji dla danego użytkownika, czas przesłania żądania, jego metodę (w naszym przypadku jest to metoda GET), kod statusu oraz informacje o użytym sposobie kodowania. Informacje te są bardzo przydatne, choć w naszym przypadku niewiele możemy z nimi zrobić.
Fragment Trace Information (informacje śledzenia) zawiera informacje o przebiegu wykonywania strony. Lista wyświetlana w tej części strony zawiera wszystkie zaistniałe i obsłużone zdarzenia oraz czasy ich realizacji (licząc od początku obsługi żądania oraz od momentu zakończenia wykonywania poprzedniego zdarzenia). Niektóre ze zdarzeń widocznych na rysunku 20.11 są nam już znane, jest jednak także kilka zdarzeń z którymi spotkaliśmy się po raz pierwszy. Kolumna Category (kategoria) informuje w jakim miejscu został wygenerowany każdy z wyświetlonych „śladów”.
Kolejny fragment strony — Control Tree (drzewo elementów sterujących) — prezentuje hierarchiczne drzewo wszystkich elementów sterujących serwera wykorzystywanych na danej stronie ASP.NET. Warto zwrócić uwagę na to, iż wyświetlane są nawet elementy LiteralControl --> (więcej informacji na ich temat można znaleźć w rozdziale 2., pt.: „Tworzenie stron ASP.NET”)[Author:p8R] . W tym fragmencie strony wyświetlana jest wielkość każdego z elementów sterujących (wyrażona w bajtach) oraz wielkość obszaru pamięci koniecznego do zapamiętania stanu danego elementu (tzw. widoku stanu). To właśnie w tym miejscu możesz się przekonać ile pamięci pochłania zapamiętanie staniu formularza i na tej podstawie zdecydować czy pozostawić, czy też wyłączyć tę opcję.
Następny fragment — Cookies Collection (kolekcja cookies) — zawiera informacje o wykorzystywanych cookies oraz ich wartościach. W zależności od natury konkretnej aplikacji, ten fragment strony może być zupełnie pusty, bądź też może prezentować jakieś informacje.
Fragment Headers Collection (kolekcja nagłówków) prezentuje informacje zapisane w nagłówkach HTTP. Między innymi są to informacje o serwerze, wersji i typie przeglądarki, adresie strony URL jaka była wyświetlona w przeglądarce użytkownika w chwili gdy przesłano żądanie, typie zawartości, i tak dalej.
Fragment Form Collection (kolekcja danych z formularza) przedstawia wszelkie dane przesłane metodą POST. Jeśli właśnie przesłałeś formularz wyświetlony na oglądanej stronie, to w tej części w tej części informacji wygenerowanych przez mechanizm śledzenia, powinieneś zobaczyć wpisane przez siebie wartości oraz informacje o widoku stanu.
I w końcu ostatni fragment wyświetlanych informacji — Server Variables (zmienne serwera) — zawiera dane o nagłówkach HTTP, nazwę i port na którym działa serwer, typ połączenia HTTP, itd. Są to informacje wykorzystywane przez serwer do skonfigurowania aplikacji.
Informacje generowane przez mechanizm śledzenia można także posortować ze względu na czas zajścia zdarzenia lub kategorię do jakiej należą. W tym celu wykorzystywany jest atrybut TraceMode dyrektywy @ Page:
<%@ Page Language="VB" Trace="true" TraceMode="SortByTime" %>
SortByTime (sortuj według czasu) to domyślny sposób sortowania informacji wyświetlanych przez mechanizm śledzenia. W przypadku sortowania wyników według kategorii (SortByCategory) są one prezentowane w inny sposób, niemniej jednak w naszym przypadku nie da się zauważyć żadnej różnicy, gdyż wykorzystywana jest tylko jedna kategoria.
Jak można się przekonać, powyższa strona zawiera wiele informacji dotyczących efektywności działania aplikacji. Można wśród nich znaleźć dokładne dane o czasie realizacji każdego etapu przetwarzania strony. Na ich podstawie można określić czy konieczne jest wprowadzanie poprawek w celu usprawnienia działania danej strony. Na przykład, warto zwrócić uwagę, iż czas pomiędzy zdarzeniami BeginRender oraz EndRender (czyli czas jaki zajęło wyświetlenie strony w przeglądarce), jest jednym z największych.
Właściwość Trace można wykorzystać do wyświetlana własnych komunikatów testowych na wynikowej stronie ASP.NET. Właściwość ta przechowuje obiekt klasy TraceContext która udostępnia dwie, niemal identyczne metody — Write oraz Warn. Obie metody wyświetlają informacje podane przez programistę, z tym iż druga z nich wyświetla je w kolorze czerwonym.
Przyjrzyjmy się prostemu przykładowi. Dodaj poniższy fragment kodu do wiersza 12. listingu 20.6 (czyli zastąp umieszczone w nim wywołanie metody Response.Write).
Trace.Write("Moja kategoria", strSQL )
Pierwszym argumentem wywołania tej metody jest kategoria, do której ma należeć wyświetlany komunikat, a drugim — treść tego komunikatu. Po przesłaniu formularza powinne zostać wyświetlone wyniki przypominające te z rysunku 20.13.
Rysunek 20.13. Dodawanie własnych informacji testowych
Zwróć uwagę na nową kategorię wyświetloną mniej więcej w połowie fragmentu strony Trace Information. Zdarzenie to zostało wygenerowane pomiędzy zdarzeniami Begin Raise PostBackEvent a End Raise PostBackEvent, które odpowiadają za obsługę przesłanego formularza. Nowy, wygenerowany przez nas komunikat zawiera zapytanie SQL zapisane w zmiennej strSQL. Należy zwrócić uwagę iż komunikat ten nie pojawił się na faktycznej stronie, a jedynie wśród informacji generowanych przez mechanizm śledzenia. Dzieje się tak dlatego, że metody Trace.Write oraz Trace.Warn zapisują komunikaty w dzienniku śledzenia, a nie w wynikach działania samej strony ASP.NET przesyłanych do klienta.
Przeanalizujmy teraz nieco bardziej złożony przykład, przedstawiony --> na listingu 20.7[Author:p8R] . Strona ta tworzy obiekt DataSet, zapisuje go w pamięci podręcznej, a następnie wiąże dane z elementem sterującym DataGrid. Podczas obsługi kolejnych żądań dane nie są pobierane z bazy danych, lecz z pamięci podręcznej, co skraca czas wykonywania strony. Na stronie jest także dostępny przycisk, który pozwala użytkownikom na unieważnienie i usunięcie zawartości pamięci podręcznej, przez co proces pobierania informacji z bazy danych zostanie ponownie wykonany.
Listing 20.7. Bardziej złożony przykład wykorzystujący mechanizm śledzenia.
<%@ Page Language="VB" Trace="true"%>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<script language="VB" runat="server">
sub Page_Load(obj as Object, e as EventArgs)
Trace.Warn("Własna", "Ładujemy stronę...")
if not Page.IsPostBack then
Trace.Warn("Własna", "Nowe żądanie, wywołujemy procedurę CreateData...")
CreateData()
end if
end sub
sub CreateData
dim source as DataView
source = Cache("DataView")
if source is nothing then
dim strConnString as string = "Provider=" & _
"Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\ASPNET\data\banking.mdb"
Trace.Warn("Własna", "Tworzymy obiekt OleDbDataAdapter...")
dim objCmd as OleDbDataAdapter = new _
OleDbDataAdapter("select * from tblUsers", _
strConnString)
Trace.Warn("Własna", "Zapytanie SQL: " & _
objCmd.SelectCommand.CommandText)
dim ds as DataSet = new DataSet()
objCmd.Fill(ds, "tblUsers")
source = new DataView(ds.Tables(0))
Trace.Warn("Własna", _
"Wstawiamy obiekt DataSet do pamięci podręcznej...")
Cache.Insert("DataView", source)
lblMessage.Text = "Dane pobrane bezpośrednio"
else
lblMessage.Text = "Dane pobrane z pamięci " & _
"podręcznej<br>"
end if
Trace.Warn("Własna", "Wiążemy dane...")
dgData.DataSource = source
dgData.DataBind()
end sub
sub ExpireCache(obj as object, e as eventargs)
Trace.Warn("Własna", "Usuwamy dane z pamięci " & _
"podręcznej, wywołujemy procedurę CreateData")
dim dv as dataview = Cache.Remove("DataView")
CreateData()
end sub
</script>
<html><body>
<form runat="server">
<asp:Label id="lblMessage" runat="server"
maintainState=false/><p>
<asp:Button id="btSubmit" runat="server"
text="Wyczyść pamięć podręczną"
OnClick="ExpireCache"/><p>
<asp:DataGrid id="dgData" runat="server"
BorderColor="black" GridLines="Vertical"
cellpadding="4" cellspacing="0"
width="450" Font-Name="Arial"
Font-Size="8pt"
/>
</form>
</body></html>
Analiza
Przede wszystkim należy zwrócić uwagę, że w 1. wierszu powyższego przykładu zostaje włączone śledzenie (poprzez użycie atrybutu Trace="true" dyrektywy @ Page). Dzięki temu na stronie będą wyświetlane informacje generowane przez mechanizm śledzenia raz nasze komunikaty wyświetlane przy użyciu metod Trace.Write i Trace.Warn.
Wywołania metody Trace.Warn zostały rozmieszczone w kluczowych miejscach strony — w wierszach: 7., 9., 23., 27., 34., 44. i 50. — aby informować nas o zachodzeniu konkretnych zdarzeń. Po jednokrotnym wyświetleniu strony i kliknięciu przycisku Wyczyść pamięć podręczną zostaną wyświetlone informacje przedstawione na rysunku 20.14.
Rysunek 20.14. Generacja informacji testowych określanych przez twórcę strony
Strona z rysunku 20.14 zawiera całkiem sporo przydatnych informacji. Przede wszystkim można na jej podstawie określić kolejność zachodzenia poszczególnych zdarzeń. W szczególności należy zwrócić uwagę, iż zdarzenie Page_Load zachodzi znacznie wcześniej niż zdarzenia związane z przetwarzaniem formularza. Wiele błędów często popełnianych przy tworzeniu stron ASP.NET wynika z faktu, iż ich autorzy zapominają o kolejności zachodzenia i obsługi zdarzeń.
Po drugie, na wygenerowanej stronie zostało wyświetlone zadawane zapytanie SQL. W naszym przypadku postać tego zapytania nie zmienia się, więc wyświetlenie go nie ma szczególnego znaczenia. Niemniej jednak możliwość ta jest niezwykle przydatna jeśli wykorzystywane zapytanie SQL musi być tworzone dynamicznie.
Kolejnym zagadnieniem na jakie należy zwrócić uwagę, jest różnica czasu pomiędzy komunikatami „Wstawiamy obiekt DataSet do pamięci podręcznej” i „Zapytanie SQL”. Określa ona czas jaki zabrało wykonanie zapytania i pobranie informacji z bazy danych. Jest on blisko 200 razy dłuższy od czasu pomiędzy dowolnymi dwoma --> generowanymi przez nas komunikatami[Author:p8R] .
A teraz odświeżmy stronę (bez powtórnego przesyłana danych formularza) i ponownie przeanalizujmy informacje wygenerowane przez mechanizm śledzenia. Zwróć uwagę, iż łączny czas wykonania strony jest ponad dwukrotnie krótszy.
I wreszcie na samym końcu przejrzyjmy część strony Control Tree i zwróćmy uwagę na ilość pamięci koniecznej do zapamiętania widoku stanu określoną w kolumnie Viewstate Size Bytes (excluding children) (rozmiar widoku stanu w bajtach (bez elementów podrzędnych)). (Więcej informacji na temat widoku stanu można znaleźć w rozdziale 2., pt.: „Tworzenie stron ASP.NET”.). Po dodaniu wszystkich wartości wyświetlonych w tej kolumnie okaże się, że strona wymaga około 6 kb pamięci, czyli niemal 40% całej pamięci koniecznej do wyświetlenia strony (podanej w kolumnie Render Size Bytes (including children) — pamięć konieczna do wyświetlenia elementu (wraz z elementami podrzędnymi)). Jeśli nie musimy korzystać z widoku stanu, to bez wątpienia wyłączenie go bardzo się opłaci; można to zrobić dodając do dyrektywy @ Page atrybut EnableViewState="false".
Śledzenie na poziomie strony jest bardzo potężnym narzędziem umożliwiającym nie tylko testowanie strony lecz także optymalizację jej działania. Jeśli informacje generowane przez mechanizm śledzenia nie będą już potrzebne, można je ukryć wyłączając śledzenie — wystarczy w tym celu dodać do dyrektywy @ Page atrybut Trace="false". Warto zauważyć, iż nie trzeba usuwać z kodu strony wywołań metody Trace.Warn — nie wpływają one bowiem na sposób wykonywania strony ani na efektywność jej działania.
Może się zdarzyć, że będziemy chcieli wykonać pewne fragmenty kodu wyłącznie w przypadku jeśli mechanizm śledzenia jest włączony. Na przykład, możemy chcieć, aby podczas śledzenia były wyświetlane generowane przez informacje testowe. W tym celu wystarczy sprawdzić wartość właściwości Trace.IsEnabled:
If Trace.IsEnabled then
For i = 0 to ds.Tables("tblUsers").Rows.Count-1
Trace.Write("Info usr.", ds.Tables("tblUsers"). _
Rows(i)(0).ToString)
next
end if
W przypadku gdy mechanizm śledzenia został włączony, to powyższy fragment kodu dodaje do generowanych przez niego informacji, dane pobrane z bazy danych. Oznacza to, że właściwość Trace można wykorzystać do określania kiedy mają być wykonywane fragmenty kod; możliwość ta jest nieodzowna podczas testowania aplikacji.
Śledzenie na poziomie aplikacji
Mechanizm śledzenia jest niezwykle przydatnym narzędziem, jednak włączanie go i wyłączanie na każdej ze stron wchodzących w skład witryny może być prawdziwym utrapieniem. Co więcej, śledzenie na poziomie strony daje dostęp do informacji dotyczących wyłącznie aktualnie wyświetlanej strony ASP.NET — nie ma wśród nich żadnych informacji o jakichkolwiek innych stronach witryny. Dzięki śledzeniu na poziomie aplikacji można w bardzo prosty sposób włączać i wyłączać mechanizm śledzenia dla wszystkich stron witryny oraz przeglądać generowane przez niego informacje w jednym, centralnym miejscu.
Aby włączyć mechanizm śledzenia na poziomie aplikacji (czyli tak, by zostały nim objęte wszystkie strony witryny), należy umieścić w pliku konfiguracyjny web.config sekcję trace:
<configuration>
<system.web>
<trace enabled="true" />
</system.web>
</configuration>
Użycie powyższego pliku konfiguracyjnego spowoduje włączenie mechanizmu śledzenia dla całej witryny, co oznacza, że mechanizm śledzenia będzie generował informacje dla każdej z odwiedzanych stron (chyba że na której ze stron zostanie on jawnie wyłączony). Domyślnie, w przypadku włączania śledzenia na poziomie witryny, informacje generowane przez mechanizm śledzenia nie są wyświetlane na wykonywanych stronach. W tym przypadku wszystkie dostępne informacje można wyświetlić przy wykorzystaniu jednego, specjalnego pliku o nazwie trace.axd. (Nie trzeba go tworzyć ręcznie — wszystkim automatycznie zajmie się ASP.NET; w rzeczywistości pliku tego nie będzie można otworzyć przy użyciu Eksploratora Windows, gdyż IIS tworzy go dynamicznie.) Plik trace.axd można wyświetlić z poziomu dowolnego folderu aplikacji:
http://localhost/aspnetdlakazdego/trace.axd
Strona wyświetlona po podaniu powyższego adresu będzie przypominać tę z rysunku 20.15, oczywiście jej zawartość będzie zależeć od stron jakie zostały wyświetlone wcześniej.
Rysunek 20.15. Informacje generowane przez mechanizm śledzenia na poziomie aplikacji udostępniane za pośrednictwem pliku trace.axd
Notatka
Nie zapomnij dodać sekcji <trace enabled="true" /> do pliku konfiguracyjnego web.config zanim zaczniesz wyświetlać strony wchodzące w skład aplikacji, gdyż w przeciwnym przypadku w pliku trace.axd nie będą dostępne żadne informacje!
Domyślnie strona trace.axd przedstawia podstawowe informacji dotyczących 10 ostatnich żądań. Wyświetlany jest czas otrzymania żądania, nazwa żądanego pliku, metoda żądania oraz kod odpowiedzi. Połączenia View Details (wyświetl informacje szczegółowe) umieszczone z prawej strony każdego wiersza umożliwiają wyświetlenie informacji wygenerowanych przez mechanizm śledzenia, przypominających te przedstawione na rysunku 20.11 (z tym iż nie będzie wśród nich właściwych wyników działania strony). Połączenie [ clear current trace ] (wyczyść informacje śledzenia) umożliwia usunięcie z dziennika aplikacji wszelkich, przechowywanych w nim informacji wygenerowanych przez mechanizm śledzenia. Informacje te zabierają sporo miejsca, a zatem usunięcie ich może nieznacznie poprawić efektywność działania aplikacji. (Warto jednak pamiętać, że liczba obsłużonych żądania może rosnąć bardzo szybko!)
A teraz otwórz nowe okno przeglądarki, wyświetl w nim dowolną stronę naszej aplikacji i kilkukrotnie ją odśwież. Następnie wróć do okna prezentującego plik trace.axd i odśwież jego zawartość. Powinne się w nim pojawić informacje o nowych żądaniach. Warto zwrócić uwagę, iż wraz z każdym nowym żądaniem wyświetlonym na tej stronie zmniejsza się liczba wyświetlona w polu Remaining (pozostaje). Jeśli przekroczymy dopuszczalny limit, to starsze żądania będą usuwane.
Informacje prezentowane w pliku trace.axd można konfigurować na wiele różnych sposobów — można zmieniać ilość zapamiętywanych żądań, sposób wyświetlania informacji oraz miejsce w jakim będą prezentowane. Poniżej przedstawiona została pełna składnia sekcji trace, zapisywanej w pliku konfiguracyjnym web.config:
<trace enabled="wartośćLogiczna" pageOutput="wartośćLogiczna"
requestLimit="liczba" traceMode="trybŚledzenia" />
Atrybut traceMode pełni tę samą funkcję co w przypadku śledzenia na poziomie strony — określa w jaki sposób powinien działać mechanizm śledzenia. Atrybut pageOutput określa czy informacje generowane przez mechanizm śledzenia mają być wyświetlane nie tylko w pliku trace.axd lecz także na poszczególnych stronach ASP.NET; domyślnie atrybut ten ma wartość false. Kolejny atrybut — requestLimit — określa ilość żądań, o których informacje będą przechowywane w pamięci; domyślna wartość tego atrybutu wynosi 10.
Notatka
Ustawienia atrybutu Trace (konkretnie Trace="false") umieszczane w dyrektywie @ Page na poszczególnych stronach ASP.NET mają wyższy priorytet niż ustawienia dotyczące śledzenia na poziomie aplikacji. Oznacza to, że jeśli wyłączymy śledzenie dla danej strony, to informacje o niej nie będą dostępne w pliku trace.axd.
Śledzenie na poziomie aplikacji zapewnia szybki i łatwy sposób dostępu do informacji dotyczących wszystkich stron witryny. Dzięki niemu można wykrywać „wąskie gardła” aplikacji w sytuacji gdy będzie ona obciążona dużą ilością żądań.
Program uruchomieniowy CLR
Testowanie kompilowanych aplikacji może wymagać zastosowania szczególnych rozwiązań; może się bowiem zdarzyć, że aplikacje te nie będą w stanie generować wyników które programista mógłby bezpośrednio zobaczyć (na przykład, skompilowane obiekty biznesowe przeważnie nie wyświetlają żadnych informacji). Zazwyczaj, kod takich aplikacji jest tłumaczony na język maszynowy lub język pośredni MSIL, które znacznie utrudniają analizę przebiegu wykonywania aplikacji. Programy tego typu wymagają użycia programu uruchomieniowego innego typu — programu, który można dołączyć do działającego procesu.
Nowe wyrażenie
Podczas uruchamiania aplikacji, jej skompilowany kod jest wykonywany przez komputer. Kod ten wykonuje polecenia, tworzy zmienne, przypisuje im wartości, a następnie je usuwa. Przypominasz sobie zapewne, że wszystkie te zmienne są przechowywane w pamięci. Dołączanie programu uruchomieniowego oznacza, że inna aplikacja — a konkretnie, właśnie program uruchomieniowy — obserwuje pamięć wykorzystywaną przesz testowany program. Program uruchomieniowy może interpretować wykonywane instrukcje oraz zawartość pamięci używanej przez testowany program i prezentować ją programiście w przejrzysty i zrozumiały sposób.
Metody testowania przedstawione we wcześniejszej części tego rozdziału wymagały modyfikowania kodu źródłowego aplikacji ASP.NET przed ich wykonanie oraz analizy wygenerowanych wyników. Dołączanie programu uruchomieniowego jest bardzo przydatnym rozwiązaniem jeśli obserwacja procesu realizacji strony i modyfikacja wykonywanych instrukcji musi następować podczas wykonywania danej strony. Różnica pomiędzy tymi dwiema metodami testowania została zilustrowana na rysunku 20.16.
Rysunek 20.16. Dołączania program uruchomieniowego pozwala na śledzenie procesu wykonywania aplikacji krok po kroku
Opis rysunku
Output debugging — Testowanie aplikacji na podstawie generowanych wyników
Modify code to … — Modyfikacja kodu polegająca na wstawieniu instrukcji testujących
Let program run until… — Wykonania programu aż do jego zakończenia
Examine output — Analiza wyników
Repeat — Powtórzenie procesu
Runetime debuggint — Testowanie programu podczas jego działania
Execute program — Wykonanie programu
Attach debugger — Dołączenie programu uruchomieniowego
Watch/alter commands … — Obserwacja i modyfikacja instrukcji i zmiennych podczas działania programu
See immediate results … — Natychmiastowa obserwacja wyników
Stop… — Zatrzymanie programu uruchomieniowego
Repeat — Powtórzenie procesu
Użycie programu uruchomieniowego CLR
Zanim będzie można użyć programu uruchomieniowego należy włączyć możliwość testowania aplikacji w pliku konfiguracyjnym web.config. W tym celu, do pliku web.config zapisanego w głównym folderze aplikacji należy dodać następujący fragment kodu:
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
</configuration>
Nowe wyrażenie
Powyższa sekcja compilation powoduje wygenerowanie pliku symboli (z rozszerzeniem .pdb) dla danej aplikacji. Plik ten dostarcza programowi uruchomieniowemu informacji koniecznych do poprawnego skojarzenia interpretowanych poleceń z kodem źródłowym aplikacji. Dzięki temu można śledzić przebieg wykonywania programu obserwując jego kod źródłowy, a nie tajemniczy i niezrozumiały kod maszynowy.
Dodanie powyższej sekcji do pliku konfiguracyjnego powoduje także wyświetlania podstawowych informacji ułatwiających testowanie bezpośrednio na stronach ASP.NET, na których wystąpił błąd (patrz podrozdział „Informacje wstępne dotyczące testowania aplikacji”).
Ostrzeżenie
Koniecznie należy wyłączyć opcje testowania aplikacji, gdy tylko proces sprawdzania poprawności jej działania zostanie zakończony. Kompilacja podczas której są generowane pliki symboli w znaczącym stopniu spowalnia działanie aplikacji.
Spróbujmy teraz wykonać program uruchomieniowy CLR. Nosi on nazwę DbgCLR.exe i domyślnie jest zapisywany w folderze c:\Program Files\Microsoft.NET\FramworkSDK\DbgCLR. Wygląd okna tego programu zaraz po uruchomieniu został przedstawiony na rysunku 20.17.
Rysunek 20.17. Interfejs graficzny programu uruchomieniowego CLR
Po uruchomieniu programu uruchomieniowego CLR można przystąpić do testowania stron ASP.NET. Proces ten składa się z czterech etapów:
otworzenia pliku, który chcemy testować;
dołączenia programu uruchomieniowego do procesu ASP.NET;
ustawienia punktów przerwań;
wykorzystania możliwości programu uruchomieniowego do manipulowania przebiegiem wykonywania aplikacji.
Zakładam, że kod z listingu 20.7 zapisałeś w pliku o nazwie listing2007.aspx; teraz wyświetl tę stronę w przeglądarce, tak aby została wykonana. W ten sposób uruchomimy proces ASP.NET (jeśli nie został uruchomiony już wcześniej). Teraz otwórz ten sam plik w programie uruchomieniowym CLR. W tym celu wybierz opcje FileOpenFile, a następnie, w wyświetlonym oknie dialogowym, odszukaj odpowiedni folder i otwórz plik listing2007.aspx. Po otworzeniu pliku jego kod źródłowy powinien się pojawić w oknie programu uruchomieniowego, a jego nazwa — w oknie Solutions Explorer wyświetlonym z prawej strony okna programu uruchomieniowego.
Aby dołączyć program uruchomieniowy do procesu ASP.NET należy wybrać z menu głównego opcję ToolsDebug Processes. Po wybraniu tej opcji na ekranie pojawi się okno dialogowe przedstawione na rysunku 20.18.
Rysunek 20.18. Dołączania programu uruchomieniowego do procesu ASP.NET
Teraz zaznacz pole wyboru Show system processes (jeśli nie jest zaznaczone) i na liście wyświetlonej powyżej niego odszukaj proces o nazwie aspnet_ewp.exe. Zaznacz ten proces po czym kliknij przycisk Attach. --> Zamknij okno dialogowe klikając przycisk Close[Author:p8R] .
Teraz w oknie programu uruchomieniowego pojawi się nowe okno — Disassembly. Okno to przedstawia zinterpretowane instrukcje kodu maszynowego testowanej strony.
Ostrzeżenie
Gdy dołączasz program uruchomieniowy do jakiejkolwiek innej aplikacji, to zazwyczaj aplikacja ta zostaje „zamrożona” (nie można z niej normalnie korzystać) a wszystkie niezapisane informacje mogą zostać utracone. ASP.NET nie stanowi pod tym względem żadnego wyjątku. Dołączając program uruchomieniowy do działających procesów upewnij się, że nie zablokujesz żadnych produkcyjnych aplikacji.
Nowe wyrażenie
Punkt przerwania to konkretne miejsce kodu programu w którym jego realizacja zostaje wstrzymana. Punkty przerwania są niezwykle przydatne w tych wszystkich sytuacjach, gdy chcemy przetestować konkretny wiersz programu. Na przykład, jeśli wiemy w którym wierszu programu powstają błędy, to możemy ustawić punkt przerwania bezpośrednio przed tym wierszem i użyć programu uruchomieniowego do przeanalizowania wykonywanych instrukcji i bieżących wartości zmiennych.
Aby ustawić punkt przerwania, kliknij szarą kolumnę wyświetloną z lewej strony kod źródłowego testowanego programu. Pojawi się w niej czerwone kółko oznaczające, że wykonywanie programu zostanie wstrzymane w danym wierszu. (Jeśli w tym czerwonym kółku pojawi się znak zapytania, może to świadczyć o tym, iż testowanie nie zostało włączone w pliku konfiguracyjnym web.config.) Przytrzymaj wskaźnik myszy w obrębie kółka, a poniżej zostanie wyświetlona dokładna lokalizacja punktu przerwania (patrz rysunek 20.19).
Rysunek 20.19. Ustawienie punktu przerwania spowoduje tymczasowe wstrzymanie wykonywania programu we wskazanym miejscu
Teraz ponownie wyświetl stronę listing2007.aspx w przeglądarce. Gdy realizacja programu dotrze do ustawionego punktu przerwania, zostanie ona zatrzymana, a uaktywni się program uruchomieniowy CLR.
Poniżej menu głównego programu uruchomieniowego CLR znajdują się elementy sterujące umożliwiające kontrolę przebiegu wykonywania testowanej strony; pozwalają ona, między innymi na przejście do następnej instrukcji programu lub zakończenie testowania. Program uruchomieniowy jest bardzo złożonym narzędziem o niezwykle rozbudowanych możliwościach. Więcej informacji na jego temat można znaleźć w dokumentacji .NET SDK.
Testowanie skompilowanych aplikacji może być procesem trudnym i nieprzyjemnym, gdyż nie można przerwać wykonywania takiego programu bez utraty podanych w nim informacji, ani obserwować wartości zmiennych w czasie jego działania. W znacznym stopniu utrudnia to określanie miejsca w jakim pojawia się błąd, zwłaszcza jeśli testowana aplikacja jest bardzo złożona. Program uruchomieniowy CLR udostępnia wszystkie narzędzia konieczne do testowania stron ASP.NET i sprawia, że proces ten staje się wyjątkowo prosty.
Notatka
Aplikacje ASP.NET nie są jedynymi aplikacjami, do których można dołączać program uruchomieniowy CLR. Program ten można dołączać i użyć do testowania wszystkich programów stworzonych w środowisku .NET. (Oznacza to, że program ten może służyć wyłącznie do testowania aplikacji napisanych w środowisku .NET; nie można go wykorzystać do testowania aplikacji przeznaczonych dla systemu Windows 95.) Trzeba tylko pamiętać, że dana aplikacja musi umożliwiać testowanie — czyli musi posiadać konieczne pliki symboli.
Zalecenia związane z testowaniem aplikacji
ASP.NET udostępnia kilka różnych metod testowania aplikacji. Ale której z nich należy użyć i w jakiej sytuacji?
Jeśli nie wiemy w jakim miejscu występują błędy, to najlepszym rozwiązaniem będzie wykorzystanie mechanizmu śledzenia. W tym przypadku, do kodu aplikacji należy dodać wywołania metod Trace.Write oraz Trace.Warn i a następnie sprawdzić które z nich są wykonywane, a które zostały pominięte. Zazwyczaj metoda ta pozwala na dokładne określenie miejsca występowania błędu, a dysponując tą wiedzą będziemy mogli go odpowiednio obsłużyć.
Instrukcje try oraz catch należy stosować jeśli nie jesteśmy pewni czy napisany fragment kodu będzie działać poprawnie oraz gdy informacje o błędach nie mogą być widoczne dla użytkownika końcowego. Instrukcje te pozwalają na przechwytywanie błędów i ich obsługę, dzięki czemu żadne niepożądane komunikaty nie będą wyświetlane.
I w końcu ostatnim dostępnym rozwiązaniem jest wykorzystanie programu uruchomieniowego CLR. Należy się nim posługiwać gdy chcemy prześledzić proces realizacji programu podczas jego działania. Dzięki temu narzędziu możemy wykonywać aplikacje krokowo — instrukcja po instrukcji i sprawdzać czy zmienne mają poprawne wartości. Program uruchomieniowy CLR to bardzo potężne narzędzie, które niezwykle ułatwia odnajdywanie i poprawianie błędów. Niemniej jednak stosowanie go ma niekorzystne efekty uboczne, takie jak możliwość utraty danych oraz różne koszty dodatkowe. Dołączają program uruchomieniowy do procesu, jego wykonywanie zostaje zamrożone — co oznacza, że proces ten nie będzie w stanie działać poprawnie; to z kolei może przysporzyć problemów zarówno aplikacjom jak i korzystającym z nich użytkownikom.
To nie jest ASP!
ASP.NET ma zdecydowanie więcej narzędzi które można wykorzystywać przy testowania aplikacji, niż tradycyjna wersja tej technologii. Wielu programistów używających wcześniejszej wersji ASP doskonale zna metodę testowania polegającą na wykorzystaniu wywołań metody Response.Write. Z metody tej także można korzystać w ASP.NET.
Jednak ASP.NET udostępnia znacznie lepsze metody testowania aplikacji. Mechanizm śledzenia udostępnia wiele informacji które można wykorzystać do testowania oraz analizy efektywności działania aplikacji. W tradycyjnej wersji technologii ASP najbliższym odpowiednikiem mechanizmu śledzenia było wykorzystanie wywołań metody Response.Write do wyświetlania informacji wykorzystywanych przez strony ASP.
Jak wiadomo strony ASP.NET są kompilowane. Daje to nam możliwość wykorzystania programu uruchomieniowego CLR do analizy działania strony ASP.NET podczas ich wykonywania. Program ten pozwala na obserwację stanu aplikacji. W tradycyjnej technologii ASP nie istniał żaden odpowiednik tego narzędzia.
W porównaniu z tradycyjną technologią ASP, narzędzia wspomagające testowanie dostępne w ASP.NET zostały w ogromnym stopniu rozbudowane i ulepszone. Teraz nie trzeba ograniczać się do skąpych możliwości funkcjonalnych udostępnianych przez metodę Response.Write, lecz można wykorzystać narzędzia testowe i uruchomieniowe z prawdziwego zdarzenia. W ten sposób programiści ASP.NET mogą tworzyć lepsze i bardziej niezawodne aplikacje.
We wcześniejszych wersjach .NET SDK program ten nosił nazwę DbgUrt.exe
2 Część I ♦ Podstawy obsługi systemu WhizBang (Nagłówek strony)
2 Dokument2
UWAGA! W rozdziale 18 nie ma ani słowa o testowaniu ani o konfigurowaniu czegokolwiek związanego z testowaniem. Proponuję zatem, zmienić drugie zdanie na: Więcej informacji na temat konfigurowania stron ASP.NET znajdziesz w rozdziale 18., pt. „Konfiguracja i wdrażanie aplikacji ASP.NET”.
UWAGA!! W oryginale była różnica pomiędzy kodem przykładu wydrukowanym w tekście książki i kodem zamieszczonym w przykładach. W tłumaczeniu użyłem wersji z przykładów, gdyż odpowiada ona wynikom pokazanym na rysunku 20.4. Zmieniły się także numery wierszy zastępowanych przez kod z listingu 20.3 (jest to tym mowa po listingu 20.2).
Zmieniłem, bo kolumny Username i Password w tabeli tblUsers zostały utworzone w rozdziale dodatkowym "Bonus Week", który nie był tłumaczony. Aby przykład działał poprawnie trzeba by kazać czytelnikom zrobić te kolumny. Prościej było zmienić nieznacznie kod przykładu.
To chyba nie zupełnie jest prawda — nie znalazłem żadnej wzmianki o tym w indeksie w oryginale ani w tekście tłumaczenia. Proponuję usunąć.
Pomijam zdanie z odwołaniem do r. 14. gdyż ćwiczenia nie są tłumaczone (zgodnie ze strategią dla tej książki).
Pomijam ostatnie zdanie gdyż dla wyników przedstawionych na rysunku 20.14 (w tłumaczeniu) nie jest prawdziwe.
Dwa poprzednie zdania tego akapitu (3. i 4.) są nie zgodne z faktycznym sposobem działania debugera więc ich nie tłumaczyłem.