<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/atom10full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><title type="html">nblog</title><subtitle type="html">Inżynieria oprogramowania - programowanie ekstremalne (XP), programowanie sterowane testami (TDD), wzorce projektowe (DPa), zasady projektowe (DPr) itd...</subtitle><id>http://zine.net.pl/blogs/nuwanda/atom.aspx</id><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/default.aspx" /><generator uri="http://communityserver.org" version="2.1.61129.2">Community Server</generator><updated>2007-09-23T23:06:00Z</updated><link rel="self" href="http://feeds.feedburner.com/nuwanda/nblog" type="application/atom+xml" /><entry><title>Nocny ReSharper 4.0</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2008/04/17/Nocny-Resharper-4-0.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2008/04/17/Nocny-Resharper-4-0.aspx</id><published>2008-04-16T22:17:00Z</published><updated>2008-04-16T22:17:00Z</updated><content type="html">&lt;p&gt;Odkąd zacząłem używać &lt;a href="http://www.jetbrains.com/resharper/"&gt;Resharpera &lt;/a&gt;moje dotychczasowe życie programisty
zmieniło się nie do poznania. Nie jest to bynajmniej długa historia, ale faktem
jest, że odbiła się piętnem na mojej codziennej pracy. &lt;/p&gt;

&lt;p&gt;W lutym wystartował proces nocnych buildów nowej wersji
Resharpera. Buildy te są &lt;a href="http://www.jetbrains.net/confluence/display/ReSharper/ReSharper+4.0+Nightly+Builds"&gt;publicznie dostępne&lt;/a&gt;. Każdy kolejny build zawiera
nową 30-dniową licencję. Dzięki temu mogę korzystać z tego wspaniałego
narzędzia i cieszyć się zaoszczędzonym czasem (przynajmniej do czasu premiery
tej wersji). Później nie pozostanie nic innego jak zakupić licencję dla siebie.
Co ciekawe warunki licencji personal pozwalają na używanie Resharpera zarówno w
domu jak i w pracy. Warunkiem jest to, żeby używała go osoba, która zakupiła
licencję. Cieszy mnie to bardzo, bo dzięki temu R# będzie mógł być zawsze
blisko mnie ;). A skoro w planach mam taki zakup to nikogo nie zdziwi fakt, że zależy mi na tym, aby powstał produkt wysokiej jakości.
Zarejestrowałem się więc w &lt;a href="http://www.jetbrains.net/jira/browse/RSRP"&gt;ich systemie śledzenia błędów&lt;/a&gt; i tam właśnie lądują
wszystkie moje uwagi i znalezione błędy. Was również zachęcam do czynnego
udziału.&lt;/p&gt;

&lt;p&gt;Dwa miesiące pacy z wersją 4.0 i ponad miesiąc z wersją 3.0
i jestem zachwycony, żeby nie powiedzieć uzależniony. Tym co jeszcze nigdy nie
spróbowali, a pracują w środowisku VSC#, polecam gorąco. Tym co używają wersji
wcześniejszej polecam wypróbowanie któregoś z nocnych buildów wersji 4.0. A ja zadaję sobie pytanie: czemu tak późno
zacząłem tego używać?!&lt;/p&gt;

&lt;p&gt;Thank you JetBrains!&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=922" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="ReSharper" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/ReSharper/default.aspx" /></entry><entry><title>Biblioteka dostępu do TFS i testy jednostkowe</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2008/04/03/biblioteka-dostepu-do-tfs-i-testy-jednostkowe.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2008/04/03/biblioteka-dostepu-do-tfs-i-testy-jednostkowe.aspx</id><published>2008-04-03T10:42:00Z</published><updated>2008-04-03T10:42:00Z</updated><content type="html">&lt;p&gt;W poście &lt;a href="http://zine.net.pl/blogs/nuwanda/archive/2008/03/11/Tfs-Spotlight-buduje-wlasny-CAB.aspx"&gt;Tfs Spotlight – buduję własny CAB&lt;/a&gt; wprowadzającym do mojego projektu &lt;a href="http://www.codeplex.com/tfsspotlight"&gt;TfsSpotlight&lt;/a&gt; wspomniałem, że
jednym z moich celów jest pisanie testów jednostkowych. Chcę w ten sposób
zobaczyć jakie problemy pojawią się podczas pisania testów jednostkowych dla
większego i bardziej skomplikowanego projektu niż te, które do tej pory
robiłem. &lt;/p&gt;
&lt;p&gt;Proces pisania testów jednostkowych nie jest łatwy i co
jakiś czas napotykam pewne problemy. Ostatni problem pojawił mi się w momencie,
gdy chciałem napisać test dla klasy prezentera, który obsługuje widok
konkretnego workitema. Źródłem danych dla tego widoku jest obiekt klasy
pochodzącej z biblioteki dostępu do &lt;a href="http://msdn2.microsoft.com/en-us/teamsystem/aa718934.aspx"&gt;TFS&lt;/a&gt; – klasa WorkItem. Jak się szybko okazało
napisanie testu jest niemożliwe, jeżeli bierzemy przypadek całkowitego
odizolowania. Wszystko to dlatego, że nie mogę ręcznie utworzyć instancji tej
klasy. Do tego potrzebne jest połączenie z serwerem (sic!). Moje zamiary spełzły
na niczym. W dodatku klasa ta jest sealed, więc &lt;a href="http://ayende.com/projects/rhino-mocks.aspx"&gt;Rhino Mocks&lt;/a&gt; robie nie radzą. Nie
mam zielonego pojęcia jak sobie z tym poradzić.&lt;/p&gt;
&lt;p&gt;Wracając do biblioteki &lt;a href="http://msdn2.microsoft.com/en-us/teamsystem/aa718934.aspx"&gt;TFS&lt;/a&gt; pozwalającej na dostęp do
serwera to należy zauważyć, że programiści i projektanci tego API zabezpieczyli
się przed nieumiejętnym jego wykorzystaniem, aby zapobiec nadmiernemu
obciążeniu serwera. Elementy takie jak Project, Query, WorkItem czy ich
kolekcje są zaimplementowane w postaci klas, które swoje dane ładują w sposób
leniwy. Tym sposobem wyświetlając w tabeli workitemy, pobierane są tylko
właściwości wyświetlane w kolumnach. Pozostałe nie zostaną ściągnięte. Takie
zachowanie na pewno ogranicza ilość danych przesyłanych z serwera, ale niestety
sposób wykonania tych elementów może pozostawiać wiele do życzenia. Wszystkie
te klasy o których mowa są sealed, a serwisy nie zostały opisane żadnymi
interfejsami definiującymi kontrakty. Wygląda na to, że w tym przypadku nie
pomyślano o Zasadzie oddzielenia zagadnień (ang. Separation of Concerns).
Podobnie jest z kontrolkami dostarczanymi w bibliotece &lt;a href="http://msdn2.microsoft.com/en-us/teamsystem/aa718934.aspx"&gt;TFS&lt;/a&gt;. Okazuje się, że
podpinając im źródło danych one i tak pytają o coś serwer (sic!)&lt;/p&gt;
&lt;p&gt;Budując system luźno powiązany praca z takimi komponentami
jest bardzo uciążliwa, a w tym przypadku skutecznie uniemożliwia mi pisanie
testów. Chociaż trzeba zaznaczyć, że jest światełko w tunelu. Od jakiegoś czasu co raz częściej słyszy się o co raz to nowszych projektach ze stajni MS, które wspominają o testach jednostkowych. Ba, wczoraj Scott Guthrie &lt;a href="http://weblogs.asp.net/scottgu/archive/2008/04/02/unit-testing-with-silverlight.aspx"&gt;ogłosił&lt;/a&gt;, że Silverlight 2 jest testowany przy pomocy testów jednostkowych (jest ich ponad 2000) i co więcej dostarczają również narzędzi do testowania własnych projektów Silverlight-owych.&amp;nbsp; Bardzo mnie ten trend cieszy, gdyż może następna wersja biblioteki dostępu do &lt;a href="http://msdn2.microsoft.com/en-us/teamsystem/aa718934.aspx"&gt;TFS&lt;/a&gt; będzie ten trend uwzględniała.&lt;br&gt;&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=903" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Testy jednostek" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Testy+jednostek/default.aspx" /><category term="Zasady projektowe" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Zasady+projektowe/default.aspx" /><category term="TFS" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/TFS/default.aspx" /></entry><entry><title>Globalizacja aplikacji i wątki</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2008/04/02/Globalizacja-aplikacji-i-watki.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2008/04/02/Globalizacja-aplikacji-i-watki.aspx</id><published>2008-04-02T12:23:00Z</published><updated>2008-04-02T12:23:00Z</updated><content type="html">&lt;p&gt;Natknąłem się na pewne zachowanie
.Net Frameworka, które było zupełnie odmienne od tego, którego się spodziewałem.
Problem dotyczy globalizacji i wątków. Okazuje się, że mając aplikację, która
jest zlokalizowana na wiele języków musimy zwrócić szczególną uwagę za każdym
razem gdy korzystamy z wątków.&lt;/p&gt;

&lt;p&gt;Ustawienia dotyczące kultury są
właściwościami wątku. W systemie Windows przy starcie wątku ustawienia kultury pobierane
są z ustawień systemowych. Zatem jeżeli uruchamiamy aplikację, jej watek otrzymuje
ustawienia użytkownika. Działanie to jest jak najbardziej oczekiwane. Okazuje
się jednak, że tworzone w aplikacji kolejne watki otrzymują ustawienia
kulturowe w taki sam sposób jak wątek pierwszy. Może nie było by w tym nic
złego, ale osobiście oczekiwałem zachowania odwrotnego – czyli że nowy wątek
otrzyma takie same ustawienia jak wątek główny. Natomiast gdy w międzyczasie zmienimy
ustawienia kulturowe pierwszego wątku to pojawiają się dodatkowe problemy.&lt;/p&gt;

&lt;p&gt;Wyobraźmy sobie aplikację, która
przed uruchomieniem wyświetla okno, w którym możemy wybrać język w jakim
chcemy, żeby się uruchomiła. Wybierając język inny niż ten, który znajduje się
w ustawieniach systemu, będziemy działać swobodnie dopóki nie aplikacja nie
zacznie korzystać z wątków. &lt;/p&gt;

&lt;p&gt;Ja, będąc niedoświadczonym
programistą, założyłem, że skoro aplikacja ma określone ustawienia kulturowe w
pierwszym wątku, to tworząc nowe wątki ustawienia te zostaną zachowane
(skopiowane z wątku, który tworzy nowe watki). Okazuje się jednak, że CLR nie
zawiera żadnego mechanizmu kontrolowania ustawień kulturowych wątków tworzonych
w danym procesie. Dlatego każdy tworzony wątek będzie miał ustawienia te
pobrane z systemu Windows.&lt;/p&gt;

&lt;p&gt;Dobrze, skoro już wiemy o tych
niuansach, to trzeba się do tego przystosować. Jeżeli ręcznie tworzymy wątki to
nie ma z tym żadnego problemu. Klasa Thread ma odpowiednie właściwości, dzięki
którym możemy ustawienia kulturowe ustawić według własnego uznania. Ja jednak
korzystam zwykle z puli wątków (klasa ThreadPool). Jak się szybko okazało klasa
ta nie ma żadnego bezpośredniego wsparcia dla uruchamiania wątków z innymi
ustawieniami kulturowymi. A szkoda. Skoro reguły są jasne, to powinniśmy chociaż
dostać jakieś przeciążenie metody QueueUserWorkItem pozwalające określić
ustawienia kulturowe uruchamianego wątku. A tak trzeba się tym ręcznie bawić i określać
ustawienia kulturowe już w metodzie, uruchomionej w osobnym wątku, co moim
zdaniem tylko wprowadza niepotrzebne zamieszanie w kodzie. Uważam, że można
było tego uniknąć udostępniając odpowiednie API.&lt;/p&gt;

&lt;p&gt;Podsumowując okazuje się, że
pracując z aplikacją, która ma być lokalizowana na inne języki musimy
szczególną uwagę zwrócić na obsługę wątków i odpowiednio ustawiać im
właściwości kulturowe. Niemniej jednak zachowanie to jest dla mnie zgoła nie intuicyjne.
Bo przecież zwykle chcemy, aby cała aplikacja działała z tymi samymi
ustawieniami kulturowymi, a przypadki odwrotne są raczej sporadyczne. A może
jest jakieś dobre uzasadnienie tego stanu rzeczy?&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=885" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="CLR" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/CLR/default.aspx" /></entry><entry><title>Tfs Spotlight v0.3</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2008/03/30/tfs-spotlight-v0-3.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2008/03/30/tfs-spotlight-v0-3.aspx</id><published>2008-03-30T15:04:00Z</published><updated>2008-03-30T15:04:00Z</updated><content type="html">Wydałem kolejną wersję mojego małego projektu – &lt;a href="http://www.codeplex.com/tfsspotlight"&gt;Tfs Spotlight&lt;/a&gt;.&lt;br&gt;&lt;br&gt;Co nowego w &lt;a href="https://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=tfsspotlight&amp;amp;ReleaseId=10449"&gt;wersji 0.3&lt;/a&gt;?



&lt;ul&gt;&lt;li&gt;Dodano możliwość otwierania poszczególnych workitemów w
osobnych zakładkach. Wystarczy kliknąć dwa razy na dany workiem i otworzy się
on w nowej zakładce.&lt;/li&gt;&lt;li&gt;Dodano możliwość otwierania workitemów po bezpośrednim
podaniu id oraz wyborze serwera. Operację tę można wykonać też bez użycia
myszy. Zdefiniowany został skrót klawiszowy Ctrl + G, który przenosi fokus do
pola, gdzie należy wpisać id workitema. Następnie wciskając klawisz Enter
możemy rozwinąć listę dostępnych serwerów, albo jeżeli mamy tylko jeden serwer,
od razu otworzyć zakładkę z workitemem o podanym id.&lt;/li&gt;&lt;li&gt;Dodano możliwość przechodzenia między zakładkami za pomocą
kółka myszy.&lt;/li&gt;&lt;/ul&gt;





&lt;p class="MsoNormal"&gt;Dla tych co już używają tego programu umieściłem też pakiet
do auto aktualizacji. Przy uruchomieniu aplikacji powinien pojawić się monit
proponujący aktualizację. To będzie próba generalna tego mechanizmu :)&lt;br&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Miłej pracy!&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=884" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Tfs Spotlight" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Tfs+Spotlight/default.aspx" /></entry><entry><title>Tfs Spotlight – buduję własny CAB</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2008/03/11/Tfs-Spotlight-buduje-wlasny-CAB.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2008/03/11/Tfs-Spotlight-buduje-wlasny-CAB.aspx</id><published>2008-03-11T11:01:00Z</published><updated>2008-03-11T11:01:00Z</updated><content type="html">&lt;p&gt;Od kilku miesięcy nic tu nie pisałem (oczywiście poza poprzednim nieplanowanym &lt;a href="http://zine.net.pl/blogs/nuwanda/archive/2008/03/03/automatyczne-podlaczanie-debuggera-do-procesu-debugger-launch.aspx"&gt;wpisem&lt;/a&gt;). Jak łatwo
się domyślić czas mi na to nie pozwalał. Z jednej strony natłok zadań w pracy
(stabilizacja finalnej wersji – &lt;a href="http://www.comarch-altum.com/"&gt;Comarch
ALTUM&lt;/a&gt; ujrzał niedawno światło dzienne na tegorocznym &lt;a href="http://www.comarch.com/en/Press+Center/Press+Information/The+Comarch+ALTUM+premiere+at+CeBit.htm"&gt;CeBicie&lt;/a&gt;)
a z drugiej strony projekt, który chciałbym opisać w tym tekście.&lt;/p&gt;

&lt;p&gt;Pierwsze odcinki z serii &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2007/07/25/the-build-your-own-cab-series-table-of-contents.aspx"&gt;Build
your own CAB&lt;/a&gt; pojawiły się już dość dawno, a ponieważ Jeremy jest moim
ulubionym bloggerem, to śledzę je od początku z zainteresowaniem i nie mogę się
doczekać następnych (ciekaw jestem ilu z Was także czyta regularnie jego teksty).
Osobiście jako zawodowy programista mam jeszcze niewielką praktykę w budowaniu
złożonych aplikacji, ale czytając ciągle różne teksty, pojawiające się na
blogach, moja głowa wypełnia się wiadomościami teoretycznymi, a po przeczytaniu
wszystkich dostępnych dotąd artykułów z Serii zapragnąłem tę wiedzę wykorzystać
w praktyce. &lt;/p&gt;

&lt;p&gt;Pracę jako programista zacząłem właśnie od
aplikacji budowanej w oparciu o &lt;a href="http://msdn2.microsoft.com/en-us/library/aa480450.aspx"&gt;Composite
Application Block&lt;/a&gt;, pracuję z nią już ponad rok, a Seria Jeremy’ego
pozwoliła mi z dystansu spojrzeć na rozwiązania zastosowane w CAB, lepiej je
zrozumieć i wykorzystać. Ponadto chęć poszerzania swoich doświadczeń i
umiejętności pchnęła mnie dalej. W Sieci można znaleźć pomysł, by Serię
opatrzyć &lt;a href="http://rmkitson.net/archive/2008/01/25/keeping-ui-construction-dry.aspx"&gt;przykładami&lt;/a&gt;.
Ten pomysł zachęcił mnie by wykorzystać zdobytą wiedzę w praktyce i wykonać
jakiś projekt open source. Tak na marginesie trzeba zauważyć, że i tak najlepszym
przykładem jest projekt &lt;a href="http://storyteller.tigris.org/"&gt;Story Teler&lt;/a&gt;
Jeremy’ego, z którego to pochodzi większość przykładów, które możemy zobaczyć w
Serii.&lt;/p&gt;

&lt;p&gt;Pomysł ten pojawił się w momencie, gdy szukałem
trochę wolnego czasu by zająć się projektem dotyczącym Team Foundation Server
Workitem Tracking, czyli po prostu zarządzaniem workitemami. Na co dzień
pracuję w środowisku &lt;a href="http://msdn2.microsoft.com/en-us/teamsystem/aa718934.aspx"&gt;TFS&lt;/a&gt; i do tego celu używam programu &lt;a href="http://www.codeplex.com/fissum/"&gt;Fissum&lt;/a&gt;. Program ten
jest naprawdę sprytny i pozwala mi lepiej wykorzystać mój czas. Gdy pobieram
wersję z repozytorium, bądź też kompiluję projekt, Team Explorer nie nadaje się
do użytku, tak jak i całe Visual Studio.
Są po prostu zablokowane. Inną sprawą jest to, że TE w ogóle jest toporny i
wolny - czytałem, że w VS2008 ma być o niebo lepiej. Pożyjemy, zobaczymy.
Program zewnętrzny, taki jak &lt;a href="http://www.codeplex.com/fissum/"&gt;Fissum&lt;/a&gt;, powala mi w tym czasie zająć się moimi workitemami.
W szczególności najczęściej korzystam z niego w fazie stabilizacji, w której
zwykle mam do czynienia z błędami, które muszę poprawić. Dzięki niemu w czasie,
gdy Visual Studio jest zajęte, mogę na boku analizować przychodzące błędy jak i
weryfikować te już istniejące. To jest mój sposób na optymalizację czasu, a
czas przecież trzeba szanować (szczególnie, gdy harmonogram ciśnie).&lt;/p&gt;

&lt;p&gt;Można zapytać: skoro tak dobrze Ci się pracuje z &lt;a href="http://www.codeplex.com/fissum/"&gt;Fissum&lt;/a&gt;,
to czemu chcesz pisać swoje oprogramowanie? Przede wszystkim chodzi o naukę.
Generalnie aplikacja ma być prosta, jednak z drugiej strony będzie na tyle
skomplikowana, że większość tematów poruszanych w Serii znajdzie w niej
zastosowanie - chociażby implementacja UI, komend, stanu menu, zakładek itd. Drugim
celem było zapoznanie się z narzędziami, które od dawna czekają na półce - mam
tu na myśli jakiś kontener IoC (w moim wypadku &lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt;) oraz bibliotekę
do logowania (&lt;a href="http://msdn2.microsoft.com/en-us/library/aa480464.aspx"&gt;Logging
Application Block&lt;/a&gt; już widziałem w &lt;a href="http://zine.net.pl/blogs/arkadiusz_wasniewski/archive/2007/09/24/konkurs-enterprise-library-logging-lab.aspx"&gt;akcji
u Arka&lt;/a&gt;, więc wypadałoby z niego skorzystać). Trzecim powodem jest to, że
projekt &lt;a href="http://www.codeplex.com/fissum/"&gt;Fissum&lt;/a&gt; mimo że jest otwarty (open source), to jednak jest zamknięty. Program
ten zawiera kilka rzeczy, które mi przeszkadzają. O jednej już &lt;a href="http://zine.net.pl/blogs/nuwanda/archive/2007/12/18/zachowywanie-stanu-okna.aspx"&gt;pisałem&lt;/a&gt;.
Ponadto mam kilka pomysłów jak można by było go usprawnić (generalnie chodzi o
użyteczność a nie o funkcjonalność). Proponowałem &lt;a href="http://blogs.developpeur.org/miiitch/"&gt;Miiitchowi&lt;/a&gt; swoją pomoc jednak
powiedział mi, że jest to jego prywatny projekt, na którym realizuje swoje
pomysły dotyczące &lt;a href="http://msdn2.microsoft.com/en-us/teamsystem/aa718934.aspx"&gt;TFS&lt;/a&gt;. Trzeba to uszanować. Na początku modyfikowałem &lt;a href="http://www.codeplex.com/fissum/"&gt;Fissum&lt;/a&gt;
do swoich potrzeb, ale jest to uciążliwe do utrzymania, gdy Miiitch wydaje nowe
wersje.&lt;/p&gt;

&lt;p&gt;W niniejszym tekście chciałbym opisać kilka problemów,
które napotkałem budując &lt;a href="http://www.codeplex.com/tfsspotlight"&gt;TfsSpotlight&lt;/a&gt; oraz znalezionych dzięki Serii
rozwiązań. Jakoś tak wyszło, że zacząłem trochę od końca. W swojej Serii Jeremy
nie poruszył jeszcze tematu Application Shell, a w momencie gdy pisałem
pierwszą wersję aplikacji nie było też tekstu o &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2008/02/15/build-your-own-cab-18-the-command-executor.aspx"&gt;Command
Executor&lt;/a&gt;, a aby zbudować podstawę, trzeba było zacząć właśnie od tych
elementów. Dlatego właśnie rozwiązania zawarte w &lt;a href="http://www.codeplex.com/tfsspotlight"&gt;TfsSpotlight&lt;/a&gt; dotyczące tych
zagadnień są tylko i wyłącznie mojego pomysłu.&lt;/p&gt;

&lt;h3&gt;Oddzielenie logiki aplikacji od formatek&lt;/h3&gt;



&lt;p&gt;Po &lt;a href="http://vimeo.com/718265"&gt;prezentacji&lt;/a&gt;
Wojtka o MVC chyba każdy się ze mną zgodzi, że budując chociażby kalkulator
należy oddzielić logikę dziedziny od sposobu prezentacji. Pierwszą i dla mnie
najważniejszą zaletą takiego podejścia jest możliwość łatwego przetestowania
logiki bez konieczności angażowania w ten proces komponentów graficznych. Nie
napisałem tego wcześniej, ale w tym projekcie jednym z większych dla mnie
wyzwań jest zadanie pisania testów jednostkowych dla jak największych części
kodu. Co z tego wyjdzie – zobaczymy.&lt;/p&gt;

&lt;p&gt;Od dłuższego czasu przy budowaniu formatek
korzystam z pewnego wariantu wzorca Model-View-Presenter. Mówię tu wariantu,
gdyż, tak jak już wspominał Wojtek na swojej prezentacji, nie ma jednej
najlepszej implementacji wzorca MVP. W moim wykonaniu widok odpowiada wzorcowi
PasiveView (&lt;a href="http://www.martinfowler.com/eaaDev/PassiveScreen.html"&gt;Fowler&lt;/a&gt;,
&lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2007/05/30/build-your-own-cab-part-4-the-passive-view.aspx"&gt;Jeremy&lt;/a&gt;),
gdyż jest to postać najbardziej przyjazna testowaniu, bo zawiera minimalną
ilość logiki. Prezenter jest główną jednostką dowodzącą, która zawiera wszelką
logikę prezentacji i obsługi danego widoku - SupervisingController (&lt;a href="http://www.martinfowler.com/eaaDev/SupervisingPresenter.html"&gt;Fowler&lt;/a&gt;,
&lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2007/05/25/build-you-own-cab-part-3-the-supervising-controller-pattern.aspx"&gt;Jeremy&lt;/a&gt;).
Z modelem natomiast bywa różnie. W prostych przypadkach są to bezpośrednio
encje reprezentujące dane, na których dany widok pracuje. W przypadkach
bardziej skomplikowanych preferuję oddzielną klasę modelu, która w
szczególności zawiera logikę obsługi danych tj. wczytywanie, zapisywanie. Ma to
szczególne znaczenie, gdy widok ma wiele źródeł danych (np. listy wyboru, które
również trzeba zasilić danymi z bazy). W takich przypadkach model zajmuje się przygotowaniem
wszystkich potrzebnych zestawów danych i odciąża tym prezentera. W niniejszym
projekcie taka skomplikowana sytuacja jeszcze nie zaszła, gdyż biblioteki TFS
dostarczają nam gotowych kontrolek do reprezentacji całych elementów, toteż
zostałem zwolniony z konieczności implementowania ich własnoręcznie.&lt;/p&gt;

&lt;p&gt;Wykorzystując MVP preferuję podejście bazujące na
bezpośrednim odwoływaniu się widoku do prezentera. W porównaniu z podejściem
opartym na zdarzeniach jest to podejście znacznie prostsze. Przede wszystkim
dlatego, że prostsza jest nawigacja po kodzie. Mając bezpośrednie odwołania do
metod możemy wykorzystać narzędzia nawigacyjne, jakie daje nam Visual Studio, i
przemieszczać się z widoku do prezentera dwoma kliknięciami myszy. Sprawa się
trochę komplikuje, jeżeli prezenter jest opisany interfejsem. Wtedy niestety VS
sobie nie radzi i pokazuje nam implementację interfejsu, a nie kod konkretnego
prezentera, czyli nie do końca tego, czego byśmy chcieli. Problemu tego nie
mają użytkownicy &lt;a href="http://www.jetbrains.com/resharper/"&gt;ReSharepera&lt;/a&gt;,
którego genialna funkcja &lt;i&gt;Go to inheritor&lt;/i&gt;
pozwala natychmiast przemieścić się do klasy implementującej dany interfejs.
Poza tym implementacja zdarzeń wymaga od nas o wiele większego nakładu pracy.
Należy przecież w widoku zaimplementować zdarzenia dla każdej możliwej do
wykonania operacji. Następnie w prezenterze trzeba do tych wszystkich zdarzeń
podpiąć odpowiednie metody. Jak dla mnie za dużo roboty.&lt;/p&gt;

&lt;h3&gt;Zarządzanie komendami&lt;/h3&gt;

&lt;p&gt;Pytanie jest proste: jak zarządzać komendami,
które użytkownik może wykonywać? Wymagania zwykle są następujące:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;
Komenda powinna być dostępna w wielu miejscach aplikacji: menu główne,
menu kontekstowe, ikona na pasku narzędzi, skrót klawiszowy, wywołanie
z kodu programu nawet w innym module.&lt;/li&gt;&lt;li&gt;
Zarządzanie stanem komendy – czy jest aktywna czy nie.&lt;/li&gt;&lt;li&gt;
Sposób uruchomienia komendy – synchroniczne czy asynchroniczne – w samej implementacji
komendy chcielibyśmy abstrahować od sposobu jej uruchomienia.

&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Jak nie trudno się domyśleć bez spójnego
mechanizmu definiowania i obsługi komend szybko zabrniemy w ślepy zaułek i
rozwijanie aplikacji stanie się nieprzyjemne. Z resztą bez odpowiedniego
mechanizmu trudno będzie w prosty sposób zarządzać choćby stanem poszczególnych
komend. Z pomocą przychodzi nam bardzo prosty wzorzec – Komenda (&lt;a href="http://codebetter.com/blogs/jeremy.miller/pages/build-your-own-cab-14-managing-menu-state-with-microcontroller-s-command-s-a-layer-supertype-some-structuremap-pixie-dust-and-a-dollop-of-fluent-interface.aspx"&gt;Jeremy&lt;/a&gt;).
Wzorzec ten wprowadza interfejs komendy – ICommand – dzięki któremu możemy wykonanie
każdej komendy zunifikować do postaci wykonania jednej metody
ICommand.Execute(). Osobiście nigdy nie wykorzystywałem jeszcze tego podejścia,
znałem je jedynie z definicji. Pierwsze pytanie jakie mi się nasunęło to jak
dana komenda ma poznać swój kontekst (czyli dane na których ma pracować)?
Przecież metoda Execute nie przyjmuje żadnego parametru! Tutaj zrozumiałem, że
aby dobrze zaimplementować ten wzorzec trzeba do tego odpowiednio nasz system
przygotować. &lt;/p&gt;

&lt;p&gt;Na przykład rozważmy interfejs programu, który
opiera się na zakładkach. Z reguły będziemy mieli jeden pasek z przyciskami i
jedno menu główne, gdzie umieścimy komendy dotyczące aktywnej zakładki. Aby móc
dostarczyć tym komendom kontekstu należy wprowadzić jakiś sposób pobierania
aktywnej zakładki, aby komenda mogła oddelegować do niej akcję. Podobnie należy
pomyśleć o innych elementach powłoki, do których będziemy chcieli mieć dostęp.
Wydzielenie odpowiednich serwisów pozwala szybko odpowiedzieć na pytanie „jak
dana komenda ma uzyskać interesujące ją dane”.&lt;/p&gt;

&lt;p&gt;Kontynuując zadanie, potrzebny jest nam teraz
spójny sposób obsługi komend. Po pierwsze potrzebujemy jednolitego sposobu przypisywania
komend do kontrolek, a po drugie jakiegoś mechanizmu pozwalającego te komendy
uruchamiać. Zajmijmy się teraz pierwszym zadaniem, a drugie omówimy sobie
trochę później. &lt;/p&gt;

&lt;p&gt;Od jakiegoś już czasu obserwuję w eterze coraz
częściej pojawiające się odwołania do tekstu Martina Fowlera o &lt;a href="http://martinfowler.com/bliki/FluentInterface.html"&gt;fluent interfaces&lt;/a&gt;.
Co więcej, zauważyć można pojawiające się implementacje wykorzystujące ten
sposób budowania interfejsów klas. Prawdę mówiąc, gdy po raz pierwszy
przeczytałem ten artykuł, idea bardzo mi się spodobała, ale nie widziałem
jeszcze konkretnego jej zastosowania. Dopiero później, używając &lt;a href="http://ayende.com/projects/rhino-mocks.aspx"&gt;Rhino Mocks&lt;/a&gt;
zauważyłem, że przecież używam właśnie fluent interface! I rzeczywiście, do
zadań konfiguracyjnych podejście to jest niezastąpione, a wynikowy kod jest
niesamowicie czytelny. Idąc za &lt;a href="http://codebetter.com/blogs/jeremy.miller/pages/build-your-own-cab-14-managing-menu-state-with-microcontroller-s-command-s-a-layer-supertype-some-structuremap-pixie-dust-and-a-dollop-of-fluent-interface.aspx"&gt;przykładem&lt;/a&gt;
Jeremy’ego konfigurację komend zaimplementowałem wykorzystując tę technikę. Z
wyniku jestem bardzo zadowolony, gdyż powstał naprawdę bardzo elastyczny, a
zarazem spójny mechanizm przypinania komend do interfejsu użytkownika. Sposób
implementacji możecie zobaczyć w kodzie (plik ConfigureMenuExpression.cs).
Poniżej przedstawię tylko wynik użycia tej klasy konfiguracyjnej.&lt;/p&gt;

&lt;p&gt;Przykład [C#] 1. Przykład wykorzystania klasy
konfiguracyjnej wykorzystującej fluent interface.&lt;/p&gt;&lt;p style="text-align:justify;"&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;ConfigureMenuExpression&lt;br /&gt;    .Execute(CommandsNames.ExitApplication)&lt;br /&gt;    .Synchronous()&lt;br /&gt;    .For(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;this&lt;/span&gt;.miExit)&lt;br /&gt;    .For(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;this&lt;/span&gt;.tsbExit)&lt;br /&gt;    .WithShortcut(Keys.F10)&lt;br /&gt;    .Enable();&lt;br /&gt;&lt;br /&gt;ConfigureMenuExpression&lt;br /&gt;    .Execute(CommandsNames.SaveAllWorkItems)&lt;br /&gt;    .For(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;this&lt;/span&gt;.tsbSaveAll)&lt;br /&gt;    .WithShortcut(Keys.Control &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;|&lt;/span&gt; Keys.Shift &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;|&lt;/span&gt; Keys.S)&lt;br /&gt;    .Disable();&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Dzięki zastosowaniu takiego mechanizmu udało mi się
zebrać całą logikę dotyczącą konfiguracji komend w jednym miejscu. Jak łatwo
zauważyć spełniłem większość wymagań postawionych wcześniej. Po pierwsze daną
komendę możemy przypisać do wielu elementów, możemy nadać skrót klawiszowy,
możemy oznaczyć jako operację synchroniczną (domyślnie komendy uruchamiane są w
trybie asynchronicznym) oraz możemy nadać komendzie początkowy stan. Wszystko w
jednym miejscu, wszystko czytelne do granic możliwości. Kontrastując to z
koniecznością odpalenia formatki w trybie projektowania, nawigowaniu po
elementach menu i sprawdzaniu w panelu właściwości czy podpięta jest
odpowiednia metoda i czy został dobrze zdefiniowany skrót klawiszowy widać jak
wiele zalet ma przedstawione tu podejście.&lt;br&gt;

&lt;h3&gt;Powłoka – application shell&lt;/h3&gt;

Chciałem, aby moja aplikacja składała się z okna
głównego (powłoki, ang. shell), które zawierać będzie podstawowe elementy takie
jak menu, pasek narzędzi z ikonami, pasek statusu oraz kontener na zakładki. W
odróżnieniu od &lt;a href="http://www.codeplex.com/fissum/"&gt;Fissum&lt;/a&gt; chciałem, aby &lt;a href="http://www.codeplex.com/tfsspotlight"&gt;TfsSpotlight&lt;/a&gt; pracował cały czas w
jednym oknie, a za pomocą zakładek pozwalał na otwieranie wielu elementów
jednocześnie. Ponadto chciałem dać użytkownikowi bezpośredni dostęp do zapytań
zdefiniowanych dla danego projektu, co zaowocowało powstaniem panelu bocznego.&lt;br&gt;&lt;br&gt;&lt;div align="center"&gt;&lt;img src="http://zine.net.pl/photos/posts_pictures/images/855/original.aspx"&gt;&lt;br&gt;&lt;/div&gt;&lt;br&gt;W moim odczuciu zadaniem powłoki jest
dostarczenie API pozwalającego na manipulację jej elementami. Dlatego właśnie
wydzieliłem z niej kilka serwisów (zarządzanie menu – komendami, zarządzanie
zakładkami, zarządzanie paskiem statusu oraz zarządzenie zakładkami).
Zdefiniowałem spójne interfejsy opisujące te usługi i dzięki temu dowolny
element systemu może mieć dostęp do elementów wspólnych.



&lt;h3&gt;Jak to wszystko ze sobą powiązać?&lt;/h3&gt;

&lt;p&gt;Każdy, kto choć trochę liznął wzorców
projektowych GoF, do tego problemu podszedłby z Singletonem pod pachą. Implementując
każdy z serwisów powłoki w postaci singletonu umożliwiamy innym elementom
systemu łatwy dostęp do instancji tych serwisów. Niestety singleton ma jedną
poważną wadę – bardzo, ale to bardzo mocno wiąże ze sobą klasy. Klasa, która
odwołuje się do elementów statycznych innej klasy jest z nią bardzo mocno
związana. Dlaczego nie chcemy takiego mocnego powiązania? Przecież miliony
programistów na całym świecie używają singletonów z powodzeniem. Otóż tak mocne
powiązania nie pozwalają efektywnie testować klas w izolacji, a przecież o to
właśnie chodzi w pisaniu testów jednostkowych – żeby poszczególne klasy
testować w izolacji. Zatem z mojego punktu widzenia i z punktu widzenia testów
jednostkowych silngleton wypada blado. Na marginesie należy dodać, że istnieją
narzędzia pozwalające testować takie sytuacje. Narzędziem takim jest &lt;a href="http://www.typemock.com/"&gt;TypeMock&lt;/a&gt;, które w środowisku praktyków TDD
jest dość &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2008/01/18/typemock-isn-t-too-powerful-and-quot-designing-for-testability-quot-is-much-more-than-merely-mocking-anyway.aspx"&gt;kontrowersyjne&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Z pomocą przychodzi nam zasada odwracania
zależności (&lt;a href="http://www.objectmentor.com/resources/articles/dip.pdf"&gt;Dependency
Inversion Principle&lt;/a&gt;) i narzędzia pozwalające tę zasadę wprowadzać w życie,
czyli kontenery IoC (Inversion of Control). Osobiście w żadnym z moich
prywatnych projektów nie korzystałem jeszcze z tego typu narzędzi. Naturalnym
moim wyborem oczywiście jest &lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt; Jeremy’ego. W momencie, gdy piszę
ten tekst, na horyzoncie jest już &lt;a href="http://blogs.msdn.com/agile/archive/2008/01/14/diab-is-now-unity.aspx"&gt;Unity&lt;/a&gt;
ze stajni Microsoftu. David Hayden zrobił &lt;a href="http://www.pnpguidance.net/Screencast/UnityDependencyInjectionIoCScreencast.aspx"&gt;screencast&lt;/a&gt;
wprowadzający w jego użycie.&lt;/p&gt;

Zasada odwracania zależności mówi, że:&lt;br&gt;&lt;ol&gt;&lt;li&gt;

Moduły
     wysokiego poziomu nie powinny być zależne od modułów niższego poziomu. I
     jedne i drugie powinny być zależne od abstrakcji.&lt;/li&gt;&lt;li&gt;
Abstrakcje
     nie powinny być zależne od szczegółów. To szczegóły powinny być zależne od
     abstrakcji.&lt;/li&gt;&lt;/ol&gt;

Klasa A zależy od klasy B, gdy klasa A wymaga
obecności klasy B podczas kompilacji. Klasa A jest nazywana klientem natomiast
klasa B – usługą. O zależnościach między klasami pisał już &lt;a href="http://www.testability.de/"&gt;Stefan Jungmayr&lt;/a&gt;. Jungmayr
wyróżnił dwa rodzaje zależności:&lt;br&gt;&lt;ol&gt;&lt;li&gt;Zależność
     od typu (ang.
     dependency on a type) oznacza, że egzemplarz usługi musi implementować
     pewien dobrze określony typ, który może być zdefiniowany za pomocą
     interfejsu, klasy abstrakcyjnej bądź konkretnej klasy.&lt;/li&gt;&lt;li&gt;
Zakodowana
     zależność (ang.
     hard-wired dependency) oznacza, że egzemplarz usługi musi być konkretnego
     typu. Zwykle odwołanie do konstruktora usługi lub elementu statycznego
     prowadzi do powstania zakodowanej zależności.&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;Aby lepiej zobaczyć o czym mówi ta zasada,
przeanalizujmy przykład dwóch klas, które zaprojektowano bez jej uwzględnienia.
&lt;/p&gt;

&lt;p&gt;Przykład [C#] 2. Przykład dwóch klas
zaprojektowanych bez uwzględnienia zasady odwracania zależności.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;class&lt;/span&gt; RefreshQueryCommand : ICommand&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; Execute()&lt;br /&gt;    {&lt;br /&gt;        ITabPresenter presenter &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; TabsService.Instance.GetActiveTab();&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;if&lt;/span&gt; (presenter !&lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;null&lt;/span&gt;)&lt;br /&gt;        {&lt;br /&gt;            presenter.HandleRefresh();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;W przykładzie 2 mamy dwie klasy. Klasa RefreshQueryCommand
reprezentuje komendę odświeżającą zakładkę. Druga klasa – TabsService –
reprezentuje serwis obsługujący zakładki i została zaimplementowana w postaci
singletonu. Klasa RefreshQueryCommand ma zakodowaną zależność do klasy
TabsService. Jest to spowodowane odwołaniem się do statycznej właściwości tej
klasy. Taka sytuacja skutecznie utrudnia nam przetestowanie komendy w izolacji.
Testując ją chcielibyśmy sprawdzić dwa przypadki: jeżeli aktywna zakładka
istnieje operacja powinna zostać do niej oddelegowana natomiast jeżeli nie to
nic nie powinno zostać wykonane. Niestety bezpośrednie odwołanie do klasy
TabsService utrudnia nam odpowiednie przygotowanie kontekstu testu.&lt;/p&gt;

&lt;p&gt;Problemy te pomaga nam ominąć zasada odwracania zależności. Dzięki
niej pozbędziemy się niechcianego odwołania do statycznej właściwości klasy
TabsService. Zasada mówi, że tak moduły wysokiego poziomu jak i moduły niskiego
poziomu powinny zależeć od abstrakcji. Spróbujmy zatem wprowadzić abstrakcję do
przykładu 2. W module wyższego poziomu – klasie RefreshQueryCommand – chcemy
abstrahować od klasy TabsService. Opiszmy zatem klasę TabsService interfejsem.
Jego definicje przedstawiono w przykładzie 3. &lt;/p&gt;

Przykład [C#] 3. Wyodrębniony interfejs klasy TabsService.&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;interface&lt;/span&gt; ITabsService&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;/// &amp;lt;summary&amp;gt;Gets active tab&amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;/// &amp;lt;returns&amp;gt;A presenter of a currently active tab&amp;lt;/returns&amp;gt;&lt;/span&gt;&lt;br /&gt;    ITabPresenter GetActiveTab();&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Mając abstrakcje spróbujemy teraz uniezależnić komendę
od konkretnej realizacji serwisu. Chcemy uniknąć odwołania do statycznej
właściwości klasy TabsService&lt;i&gt; &lt;/i&gt;i pozwolić innej klasie podjąć decyzję o
konkretnej realizacji. Do tego celu stworzymy konstruktor, który jako parametr
będzie brał implementacje interfejsu ITabsService. Ulepszoną realizację klasy RefreshQueryCommand
wykorzystującą klasę TabsService poprzez interfejs przedstawiono w przykładzie
4. 

&lt;p&gt;Przykład [C#] 4. Ulepszona realizacja klas RefreshQueryCommand.&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;class&lt;/span&gt; RefreshQueryCommand : ICommand&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; ITabsService TabsService { get; set; }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; RefreshQueryCommand(ITabsService tabsService)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;this&lt;/span&gt;.TabsService &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; tabsService;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; Execute()&lt;br /&gt;    {&lt;br /&gt;        ITabPresenter presenter &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;this&lt;/span&gt;.TabsService.GetActiveTab();&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;if&lt;/span&gt; (presenter !&lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;null&lt;/span&gt;)&lt;br /&gt;        {&lt;br /&gt;            presenter.HandleRefresh();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Na czym polega odwrócenie zależności
przedstawione w przykładzie 4? Zauważmy, że w obecnej sytuacji klasa RefreshQueryCommand
nie zależy już od klasy TabsService, ale od interfejsu ITabsService. Odwrócenie
zależności polega na tym, że teraz klasa TabsService również zależy od interfejsu
ITabsService. Zatem zarówno klasy wyższego jak i niższego poziomu zależą od
abstrakcji a nie od konkretnych realizacji.&lt;/p&gt;

&lt;p&gt;Wśród narzędzi do zarządzania zależnościami
możemy przebierać. Począwszy od wspomnianego &lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt;, czy Unity, po &lt;a href="http://www.springframework.net/"&gt;Spring.Net&lt;/a&gt; oraz &lt;a href="http://www.castleproject.org/container/index.html"&gt;Castle Windsor&lt;/a&gt; (w
szczegóły wprowadzi Was Michał &lt;span class="nfakpe"&gt;Harasimowicz na XXII
spotkaniu wg.net – mam nadzieję, że pojawi się jakieś nagranie z tej sesji :).&lt;/span&gt;
Ja swój projekt oparłem o pierwszy z nich. Zasada działania jest banalnie
prosta. Najpierw musimy wrzucić do kontenera zabawki, aby potem pojawiły się
automatycznie, gdy będziemy ich potrzebować. Większość z kontenerów obsługuje
dwa typy wstrzykiwania implementacji: poprzez konstruktor oraz poprzez settery.
Ja osobiście wolę pierwszy ze sposobów, ale to kwestia wyboru.&lt;/p&gt;

&lt;p&gt;Zwykle, budując system luźno powiązany, opieramy
się na interfejsach definiujących kontrakty. Tak też zrobiłem w moim projekcie.
Każdy ze wspomnianych wcześniej serwisów powłoki opisany został interfejsem. Po
zarejestrowaniu konkretnych implementacji tych interfejsów w kontenerze mogę
bez ograniczeń z nich korzystać. Teraz, jeżeli dana klasa potrzebuje instancji
danego serwisu, to do jej konstruktora dodaję parametr o typie będącym interfejsem
tego serwisu. Zmienia się tylko sposób tworzenia instancji klas. Teraz zamiast
używać słowa kluczowego &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;new&lt;/span&gt; &lt;/span&gt;&lt;/code&gt; skorzystać należy z kontenera, aby ten przygotował
na instancję żądanej klasy. Zadaniem kontenera jest znalezienie konstruktora i
wypełnienie go wszystkimi wymaganymi parametrami. Parametry te są rozpoznawane
na podstawie typów. W taki oto magiczny sposób zabawki automatycznie pojawiają
się w piaskownicy.&lt;/p&gt;

&lt;p&gt;Dzięki wykorzystaniu kontenera możemy budować
klasy, które wszystkie swoje zależności otrzymują z zewnątrz. Oznacza to, że
klasy te, same nie tworzą innych obiektów potrzebnych im do współpracy. Patrząc
z punktu widzenia testów jednostek potencjalny zysk jest wielki. Teraz, aby
daną klasę przetestować w izolacji, wystarczy, że utworzymy jej instancję z
zależnościami będącymi obiektami zastępczymi (ang. mock objects). Dzięki temu
testując możemy się skupić na kodzie tylko i wyłącznie testowanej klasy, a nie
usług, od których testowana klasa zależy.&lt;/p&gt;

&lt;p&gt;Pozbywając się singeltonów tracimy także kontrolę nad
tym ile instancji danej klasy powstanie. A co jeżeli rzeczywiście chcieliśmy,
aby dana klasa miała tylko jedną instancję? Otóż nic prostszego. Kontener daje
nam możliwość określenia w jaki sposób instancja danej klasy ma być
kontrolowana. Sposób wykonania tego z użyciem &lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt; opisał Jeremy na &lt;a href="http://structuremap.sourceforge.net/SingletonInjection.htm"&gt;stronie&lt;/a&gt;
projektu.&lt;/p&gt;&lt;p&gt;

&lt;/p&gt;&lt;h3&gt;Uruchamianie komend - command runner&lt;/h3&gt;

&lt;p&gt;Nie wiem jak pozostałe kontenery, ale &lt;a href="http://structuremap.sourceforge.net/Default.htm"&gt;StructureMap&lt;/a&gt;
ma bardzo fajną opcję rejestrowania instancji konkretnych klas pod pewną
ustaloną nazwą. Później, chcąc otrzymać tę nazwaną instancję możemy zapytać
kontener podając mu właśnie tę nazwę. Opcja ta jest o tyle sprytna, że pozwala
w banalny sposób spełnić pozostałe wymagania dotyczące obsługi komend, o której
pisałem wcześniej.&lt;/p&gt;

&lt;p&gt;Jak można było zauważyć wcześniej przy
konfiguracji komend, nigdzie nie podaliśmy ani klas, ani metod obsługujących
konkretne komendy. Jedyne co widzieliśmy to stała w postaci CommandsNames.ExitApplicationCommand.
Aby zbudować system elastyczny, który będzie spełniał wszystkie wymagania, nie
mogliśmy podczas konfiguracji komend podać ich konkretnych implementacji
chociażby dlatego, że implementacja danej komendy wcale nie musi znajdować się w
klasie dostępnej podczas kompilacji. Dlatego właśnie do identyfikacji komend
używamy stałych napisowych. Stałych tych używamy przy rejestracji implementacji
komend w kontenerze, a dzięki temu znając nazwę komendy możemy później zapytać
kontener o jej instancję.&lt;/p&gt;

&lt;p&gt;Przykład [C#] 5. Realizacja klasy uruchamiającej komendy.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;class&lt;/span&gt; CommandRunner : ICommandRunner&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; Run(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; commandName)&lt;br /&gt;    {&lt;br /&gt;        Run(commandName, &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;true&lt;/span&gt;);&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; Run(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; commandName, &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;bool&lt;/span&gt; runInAsync)&lt;br /&gt;    {&lt;br /&gt;        var command &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; ObjectFactory.GetNamedInstance&amp;lt;ICommand&amp;gt;(commandName);&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;if&lt;/span&gt; (runInAsync)&lt;br /&gt;        {&lt;br /&gt;            ThreadPool.QueueUserWorkItem(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;delegate&lt;/span&gt; { command.Execute(); }, &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;null&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;else&lt;/span&gt;&lt;br /&gt;        {&lt;br /&gt;            command.Execute();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Dzięki opisaniu komend wspólnym interesem
implementacja klasy CommandRunner staje
się banalnie prosta. Ponadto klasa ta też została opisana intefjesem i
zarejestrowana w kontenerze dzięki czemu nawet klasy z innych assembly mogą się
do niej odwoływać i wykonywać komendy.

&lt;/p&gt;&lt;p&gt;Ktoś z Was może zauważyć, że rzeczywiście staramy
się tworzyć system luźno powiązany a jednocześnie korzystamy z kontenera, który
dostarcza implementacji wymaganych typów za pomocą statycznej klasy
ObjectFactory (sic!). Prawda jest taka, że niestety kontener nie może
dostarczyć instancji samego siebie więc, akurat w tym przypadku nic nie możemy
zrobić. Dlatego trzeba ograniczać miejsca, gdzie będziemy się bezpośrednio
odwoływać do niego. Dobrą praktyką, pozwalającą łatwo testować takie odwołania,
jest umieszczenie tego odwołania w metodzie wirtualnej, którą na czas testów
będziemy mogli nadpisać i zwrócić z niej to, czego będziemy potrzebowali w
kontekście testu.&lt;/p&gt;

&lt;h3&gt;Podsumowanie&lt;/h3&gt;

&lt;p&gt;Zachęcam wszystkich, którzy jeszcze nie czytali
Serii, by w wolnej chwili to zrobili. Zachęcam w szczególności tych, którzy
podobnie jak ja, pracują z CAB na co dzień. Nie chodzi oczywiście o to by każdy
z nas budował własny CAB, ale by poznać techniki i koncepcje pozwalające pisać
lepszy kod i lepiej projektować systemy.&lt;/p&gt;

&lt;p&gt;Projekt, którego część tutaj opisałem, na pewno
nie jest idealny, ale jego budowanie przynosi mi wiele satysfakcji. Na pewno w
miarę wolnego czasu będę go rozwijał. Jeżeli komuś z Was się przyda to tym
lepiej. Wszystkie uwagi są oczywiście mile widziane, tak do samej architektury,
jak i funkcji programu.&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=856" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Visual Studio" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Visual+Studio/default.aspx" /><category term="TDD" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/TDD/default.aspx" /><category term="Testowanie oprogramowania" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Testowanie+oprogramowania/default.aspx" /><category term="Testy jednostek" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Testy+jednostek/default.aspx" /><category term="Mock Objects" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Mock+Objects/default.aspx" /><category term="Inżynieria oprogramowania" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/In_7C01_ynieria+oprogramowania/default.aspx" /><category term="Zasady projektowe" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Zasady+projektowe/default.aspx" /><category term="Tfs Spotlight" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Tfs+Spotlight/default.aspx" /><category term="Wzorce projektowe" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Wzorce+projektowe/default.aspx" /></entry><entry><title>Automatyczne podłączanie debuggera do procesu - Debugger.Launch();</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2008/03/03/automatyczne-podlaczanie-debuggera-do-procesu-debugger-launch.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2008/03/03/automatyczne-podlaczanie-debuggera-do-procesu-debugger-launch.aspx</id><published>2008-03-03T20:07:00Z</published><updated>2008-03-03T20:07:00Z</updated><content type="html">&lt;p&gt;Od jakiegoś czasu mieliśmy w projekcie jeden dość uciążliwy problem: MasterSolution z blisko trzydziestoma projektami. Jak nie trudno się domyślić było to powodem kilku problemów: długi czas kompilacji, problemy z wydajnością ReSharpera itd. Na szczęście od dziś mogę pracować już tylko z podzbiorem projektów, które mnie interesują, a to wszystko dzięki odpowiedniemu podziałowi MasterSolution. &lt;/p&gt;  &lt;p&gt;Rozbicie MasterSolution zmienia niestety sposób w jaki pracowałem do tej pory. Z modelu "Edytuj i F5 (debuguj)" muszę przejść na trochę bardziej skomplikowany "Edytuj, zbuduj, uruchom i podłącz debuggera". Nowy model, choć bardziej skomplikowany, nie będzie bardziej czasochłonny z wyjątkiem jednej monotonnej czynności. Za każdym razem, gdy aplikacja się załaduje, trzeba będzie podłączyć debuggera (Debug-&amp;gt;Attach debugger). Jest to o tyle monotonne, że trzeba się trochę naklikać. Pierwsza myśl jaka mi przyszła do głowy to: czy nie da się tego zrobić automatycznie?&lt;/p&gt;  &lt;p&gt;&lt;img style="margin:10px 10px 10px 0px;" src="http://zine.net.pl/photos/posts_pictures/images/833/original.aspx" align="left"&gt;&lt;/p&gt;  &lt;p&gt;Odpowiedź jest częściowo twierdząca, a dokładnie jest nim wywołanie jednej metody klasy &lt;a href="http://msdn2.microsoft.com/en-us/library/system.diagnostics.debugger.aspx" target="_blank"&gt;System.Diagnostics.Debugger&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;Debugger.Launch();&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;/p&gt;  

&lt;p&gt;To jedno wywołanie uruchamia debuggera i podłącza go do procesu, z którego pochodzi powyższe wywołanie. Rozwiącanie jest częściowe, ponieważ i tak zmusza nas do kilku kliknięć. Po wywołaniu powyższej linii zobaczymy takie oto znajome okienko wyboru debuggera (po lewej).&lt;/p&gt;

&lt;p&gt;Gdy po raz pierwszy mi się to okienko pokazało pomyślałem sobie, że mi się aplikacja wywaliła, ale pokrótce okazało się, że okno jest efektem pożądanym i pozwala wybrać debuggera, którego chcemy użyć. Po tym jak dokonamy wyboru, wybrana przez nas instancja Visual Studio przejdzie w tryb debug, tak jakbyśmy mieli ustawiony breakpoint. I voila! Podpięliśmy się do interesującego nas procesu.&lt;/p&gt;

&lt;p&gt;Bardzo mi się to rozwiązanie podoba, gdyż zaoszczędzi mi monotonnego klikania. Wywołanie debuggera można umieścić zaraz przy starcie aplikacji, lub też wstawiać w pożądanym miejscu, tylko na czas debugowania.&lt;/p&gt;

&lt;p&gt;Jest jeszcze jedna metoda klasy Debugger, o której chciałbym wspomnieć. &lt;/p&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;Debugger.Break();&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;p&gt;Wywołanie to jest tożsame w ustawieniem breakpointa bezpośrednio w Visual Studio. Zatem czy programowe zatrzymywanie programu może nam się przydać?&lt;/p&gt;

&lt;p&gt;Otóż ciekawym zastosowaniem może być sytuacja, gdy w pewnym miejscu programu wiemy, że pewien jego stan jest nieprawidłowy. Jest wiele sposobów na takie miejsca. Możemy na przykład wypisywać komunikat na konsolę. Jeżeli jednak zachowanie to jest niepoprawne, to zwykle będziemy chcieli sprawdzić dlaczego aplikacja znalazła się w danym stanie. Prawdopodobnie wrócimy i ustawimy breakpoint by zbadać stan interesujących nas obiektów. Zatem dlaczego od razu w momencie wystąpienia tego zdarzenia nie zatrzymać programu?&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;Zatem miłego debugowania!&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=834" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Visual Studio" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Visual+Studio/default.aspx" /><category term="Tips&amp;amp;Tricks" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Tips_2600_amp_3B00_Tricks/default.aspx" /><category term="Debugging" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Debugging/default.aspx" /></entry><entry><title>Zachowywanie stanu okna</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/12/18/zachowywanie-stanu-okna.aspx" /><link rel="enclosure" type="application/zip" length="1348" href="http://zine.net.pl/blogs/nuwanda/attachment/663.ashx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/12/18/zachowywanie-stanu-okna.aspx</id><published>2007-12-18T22:00:00Z</published><updated>2007-12-18T22:00:00Z</updated><content type="html">&lt;p&gt;Czasami, pisząc własne aplikacje Windows Forms lub używając takich czcielibyśmy, aby aplikacja była choć trochę bardziej spostrzegawcza i np. zapamiętywała atrybuty okna (położenie, rozmiar oraz stan) gdy przestajemy z nich korzystać. Skoro je przestawiliśmy to znaczy, że tam nam lepiej pasują. Dlatego aplikacja zamiast z uporem pokazywać nam okno zawsze w tym samym miejscu mogła by umilić nam życie. Szczególnie, że z programistycznego punktu widzenia nie jest to operacja trudna i pracochłonna.&lt;/p&gt;  &lt;p&gt;Ostatnio w mojej codziennej pracy z &lt;a href="http://msdn2.microsoft.com/en-us/teamsystem/aa718934.aspx"&gt;TFS&lt;/a&gt; korzystam z małego narzędzia - &lt;a href="http://codeplex.com/Fissum/"&gt;Fissum&lt;/a&gt; - dającego mi dostęp do zapytań o WorkItemy poza Visual Studio. Dzięki temu, gdy przeprowadzam kompilację, albo pobieram źródła mogę sobie na boku zarzązać WorkItemami. Wszystko by było fajnie, gdyby nie denerwująca przypadłość tej aplikacji. Codziennie, gdy ją odpalam muszę zmienić pozycjęo okna, z którego najczęściej korzystam, ponieważ aplikacja z uporem pokazuje je na środku ekranu oraz w wielkości jaka mnie nie satysfakcjonuje. Postanowiłem to zmienić ;)&lt;/p&gt;  &lt;p&gt;Mając na uwadze przyszłe aplikacje, chciałem zbudować rozwiązanie, które będę mógł łatwo wielokrotnie wykorzystywać. Ponadto chciałem zminimalizować ilość kodu jaki będzie potrzebny, aby dany program wzbogacić o funkcję zapamiętywania stanu okien. Dodatkowym atutem rozwiązania byłoby zachowywanie wyżej wymienionych atrybutów w zależności od zalogowanego użytkownika.&lt;/p&gt;  &lt;p&gt;Wykorzystując mechanizm &lt;a href="http://msdn2.microsoft.com/en-us/library/3ak841sy(VS.80).aspx"&gt;Isolated Storage&lt;/a&gt; oraz serializację zbudowałem bardzo proste rozwiązanie, które łatwo wykorzystać w dowolnej aplikacji Windows Forms. Użycie jest banalnie proste. Po tym jak utworzymy obiekt okna podajemy go do specjalnej klasy, która na podstawie identyfikatora (również podanego) spróbuje pobrać zapisane dane o stanie. Jeżeli takie znajdzie to aplikuje je do podanego okna. Wywołanie ma następującą postać:&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;PersistedWindow.Prepare(myForm, &lt;span style="color: #666666;background-color: #e4e4e4;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;"FormIdentifier"&lt;/span&gt;);&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;/p&gt;  


&lt;p&gt;Sprawa jest prosta.&lt;/p&gt;

&lt;p&gt;W załączniku znajdują się dwie klasy implementujące powyższe funkcje.&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=663" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Windows Forms" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Windows+Forms/default.aspx" /><category term="Kod źr&amp;#243;dłowy" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Kod+_7A01_r_26002300_243_3B00_d_4201_owy/default.aspx" /></entry><entry><title>Grupy pasjonackie we Francji</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/12/05/grupy-pasjonackie-we-francji.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/12/05/grupy-pasjonackie-we-francji.aspx</id><published>2007-12-05T17:55:58Z</published><updated>2007-12-05T17:55:58Z</updated><content type="html">&lt;p&gt;Od jakiegoś czasu z zainteresowaniem i zadowoleniem obserwuję powstawanie zorganizowanej polskiej społeczności skupionej wok&amp;#243;ł technologii Microsoftu. Nie wszyscy z Was pewnie wiedzą, że od września jestem w Lyonie i to stamtąd oglądam to co dzieje się w Polsce. Na razie trzymam się z boku nie mogąc osobiście uczestniczyć w spotkaniach. Wyjechałem z Wrocławia na kr&amp;#243;tko przed tym jak narodziła się tam grupa, trochę szkoda. No ale nie wszystko stracone. Jeszcze tam wr&amp;#243;cę! :D&lt;/p&gt;  &lt;p&gt;Tymczasem chciałem jakoś zadomowić się tutaj. Zachęcony tak prężnymi działaniami rodzimych grup pomyślałem sobie, że na zachodzie może być tylko lepiej i w tak dużym mieście jak Lyon (drugie pod względem wielkości we Francji) na pewno znajdę interesującą grupę. Nie mogę spoytkać się z Wami to chociaż po części odbiję sobie tutaj, a co! A nic! Właśnie nic... Przeszukując pobieżnie Sieć nie znalazłem żadnej grupy z Lyonu...&lt;/p&gt;  &lt;p&gt;Zacząłem standardowo - INETA. Wynik wyszukiwania to dziesięć pozycji z kt&amp;#243;rych większość to grupy studenckie usytuowane przy politechnikach, niestety żadnej z Lyonu. Druga pr&amp;#243;ba to Culminis. Na stronie jest bardzo użyteczna mapa z naniesionymi grupami. Przybliżając teren Francji zn&amp;#243;w znajdujemy mniej więcej dziesięć grup rozstrzelonych po kraju. Niestety, ale w okolicach Lyonu panuje chyba jakaś zaraza, bo w promieniu 300km nic nie znajdziemy. Przeszukiwanie za pomocą Googli też nie daje ciekawych wynik&amp;#243;w...&lt;/p&gt;  &lt;p&gt;Z moich pobieżnych poszukiwań wynika, że Francuzi pod względem organizacji grup pasjonackich pozstają daleko w tyle za Polską. Trochę szkoda, bo myślałem, że będę m&amp;#243;gł zobaczyć jak to wygląda tu na miejscu i może czegoś ciekawego się nauczyć. Nie pozostaje mi nic innego jak cierpliwie czekać na lipiec 2008 kiedy to zamierzam wr&amp;#243;cić do Polski...&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=651" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Og&amp;#243;lne" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Og_26002300_243_3B00_lne/default.aspx" /><category term="Communities" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Communities/default.aspx" /></entry><entry><title>Code snippets</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/11/12/visual-studio-code-snippets-snippet.aspx" /><link rel="enclosure" type="application/zip" length="1146" href="http://zine.net.pl/blogs/nuwanda/attachment/609.ashx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/11/12/visual-studio-code-snippets-snippet.aspx</id><published>2007-11-12T20:34:00Z</published><updated>2007-11-12T20:34:00Z</updated><content type="html">&lt;p&gt;Chyba każdy programista korzystający z Visual Studio wie o code snippets i z nich korzysta. Na ich temat mówiło się dużo, najwięcej jeszcze przed premierą Visual Studio 2005. Najczęściej wykorzystywanym skrótem jest pewnie &lt;strong&gt;prop&lt;/strong&gt;, który pozwala nam wstawić właściwość get/set wraz z polem prywatnym, ale i inne pewnie też mają wzięcie ;).&lt;/p&gt;  &lt;p&gt;Na potrzeby mojej codziennej pracy chciałem sobie napisać parę takich snippetów, ponieważ dzięki nim nie będę musiał zaprzatać sobie głowy standardami kodowania przyjętymi w projekcie, w którym pracuję. Myślę, że pozwolą mi one pisać kod szybciej.&lt;/p&gt;  &lt;p&gt;Do tej pory używałem tylko tych dostarczonych wraz z VS. Teraz przyszła pora na zainteresowanie się tym jak one wyglądają od środka i co można z nimi fajnego zrobić. &lt;/p&gt;  &lt;p&gt;W pracy każdą klasę muszę opatrywać atrybutem Author. Pomyślałem więc, że zrobię sobie snippeta, który pozwoli mi dodawać ten atrybut. Ponadto w &lt;a href="http://msdn2.microsoft.com/en-us/library/ms165393%28VS.80%29.aspx"&gt;dokumentacji&lt;/a&gt; widziałem, że snippety potrafią dodawać referencje w projekcie a także wstawiać deklaracje using. Bardzo mi się to sposobało, ponieważ atrybut ten leży w przestrzeni nazw, dla której nie ma klauzuli using w szablonie klasu C#. Jakie było moje zdziwienie, gdy się okazało, że snippety oczywiście potrafią wstawiać referencje i usingi, ale tylko w projektach VB :(. Wielka szkoda! (może ktoś z Was wie dlaczego?). Nie mając w zanadrzu tak poręcznej opcji chyba będę zmuszony do zdefiniowania własnego snippetu dla całej klasy.&lt;/p&gt;  &lt;p&gt;W Internecie znaleźć można wiele programów pozwalających tworzyć snippety (np. &lt;a href="http://www.codeplex.com/Snipper"&gt;Snipper&lt;/a&gt;, który ma fajne kontekstowe podpowiedzi), ale tak naprawdę czy rzeczywiście potrzeba nam okienek, aby napisać kawałek xml'a? Przeglądając &lt;a href="http://msdn2.microsoft.com/en-us/library/ms165393%28VS.80%29.aspx"&gt;dokumentację&lt;/a&gt; zobaczyłem, że snippety można również definiować dla plików xml (oprócz C# i VB). Szybka konotacja doprowadziła do tego,&amp;nbsp; że w mojej głowie zrodził się pomysł, aby zrobić snippet zawierający szablon snippeta!. Jak pomyslałem tak zrobiłem i oto powstał snippet, którego znajdziecie w załączniku do tej notki. Generowany kod ubrałem w komentarze umieszczając w nich pomocne informacje, aby tworzenie snippetów było maksymalnie przyjemne :)&lt;/p&gt;  &lt;p&gt;Mam nadzieję, że i Wam się przyda, gdy będziecie chcieli stworzyć własnego snippeta. Teraz, gdy zauważam fragment kodu, który często powtarzam, mogę z kilka minut utworzyć odpowiedniego snippeta. Po prostu otwieram nowy plik xml, wywołuję snippeta dla snippeta, wypełniam pola oraz uzupełniam szblon.&lt;/p&gt;&lt;p&gt;A tu znajdziecie krótki &lt;a href="http://www.nuwanda.ovh.org/res/nblog_vssnippets_video.wmv"&gt;screencast&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=609" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Visual Studio" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Visual+Studio/default.aspx" /><category term="Tips&amp;amp;Tricks" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Tips_2600_amp_3B00_Tricks/default.aspx" /><category term="Screencast" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Screencast/default.aspx" /></entry><entry><title>SQL: Mały problem i pytanie (jak pobrać ID wstawionych wierszy)</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/10/26/SQL-jak-pobrac-id-wstawionych-wierszy.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/10/26/SQL-jak-pobrac-id-wstawionych-wierszy.aspx</id><published>2007-10-26T16:59:00Z</published><updated>2007-10-26T16:59:00Z</updated><content type="html">Spotkałem się dziś z małym problemem, który nie do końca wiem jak rozwiązać (zrobiłem pewne obejście, ale mi się nie podoba).&lt;br&gt;&lt;br&gt;Mam
tabelę dokumentów (D), tabelę elementów tych dokumentów (E) oraz tabelę
pewnych dodatkowych informacji o tych elementach (R). Relacje wyglądają
następująco: R --FK--&amp;gt; E --FK--&amp;gt; D.&lt;br&gt;&lt;br&gt;A teraz zadanie:&lt;br&gt;- Należy scalić pewny zbiór dokumentów z (D) - nagłówki scalamy w jakiś sposób (to nas nie interesuje)&lt;br&gt;- W wynikowym dokumencie mają się znaleźć elementy (E) z wszystkich scalanych dokumentów&lt;br&gt;- Dodatkowe informacje z (R) mają zostać skopiowane i przypisane do odpowiednich elementów wynikowego dokumentu&lt;br&gt;- Dokumenty źródłowe mają pozostać w niezmienionej formie&lt;br&gt;&lt;br&gt;Moja
procedura iteruje sobie po dokumentach do scalenia. Najpierw scala
nagłówek (D) kolejnego przetwarzanego dokumentu a następnie ma za
zadanie skopiować elementy tego dokumentu do dokumentu wynikowego.
Problem pojawia się z jednoczesnym skopiowaniem elementów (E) jak i
informacji rozszerzonych (R). Chciałbym jednym insertem załatwić kopię
elementów dokumentu a następnie drugim kopię informacji rozszerzonych.
Niestety po krótkich poszukiwaniach nie znalazłem rozwiązania na
pobranie identyfikatorów wstawionych wierszy (da się w ogóle?). Najszybszym rozwiązaniem
byłby kursor, ale chciałbym uniknąć kolejnego.&lt;br&gt;&lt;br&gt;Najpierw
pomyślałem o modyfikowalnym widoku (ang. updatable view), tak aby jednym insertem wrzucić element i info rozszerzone, ale że nigdy
tego nie robiłem, to szybko okazało się, że oczywiście można wstawiać
wartości do takiego widoku, nawet jeżeli składa się z wielu połączonych
tabel (w moim przypadku join między E i R), ale tylko w przypadku, gdy
operacja wstawiania dotyczy kolumn tylko jednej z tabel (czyli już nie
dla mnie). O pozostałych wymaganiach co do modyfikowalnych widoków
można poczytać na stronie &lt;a href="http://msdn2.microsoft.com/en-us/library/aa258253%28SQL.80%29.aspx"&gt;MSDN&lt;/a&gt; w podpunkcie o tytule &lt;b&gt;updatable views&lt;/b&gt;. Można też zrobić triggera &lt;i&gt;instead of insert&lt;/i&gt; dla tego widoku i w nim zająć się odpowiednim wstawianiem wartości, ale trigger to dla tego problemu chyba armata...&lt;br&gt;&lt;br&gt;Może macie jakieś pomysły? Na pewno!&lt;br&gt;&lt;br&gt;&lt;b&gt;Doprecyzowanie zadania:&lt;/b&gt;&lt;br&gt;Kolumny (duże uproszczenie):&lt;br&gt;Tabela D: ID, Data&lt;br&gt;Tabela E: ID, FK_D, Kwota&lt;br&gt;Tabela R: ID, FK_E, Info&lt;br&gt;&lt;br&gt;Dane:&lt;br&gt;Tabela D:&lt;br&gt;1, 12/01/2007&lt;br&gt;2, 13/01/2007&lt;br&gt;&lt;br&gt;Tabela E:&lt;br&gt;1, 1, 23.4&lt;br&gt;2, 1, 1.1&lt;br&gt;3, 2, 45.32&lt;br&gt;4, 2, 1.0&lt;br&gt;&lt;br&gt;Tabela R:&lt;br&gt;1, 1, 'Info'&lt;br&gt;2, 2, 'Ala'&lt;br&gt;3, 3, 'Info inne'&lt;br&gt;4, 4, 'Kot'&lt;br&gt;&lt;br&gt;Po scaleniu dokumentów 1 i 2 z D powstają dodatkowe wiersze w odpowiednich tabelach (dla nagłówka datę bierzemy najwcześniejszą)&lt;br&gt;Tabela D:&lt;br&gt;3, 12/10/2007&lt;br&gt;&lt;br&gt;Tabela E:&lt;br&gt;5, 3, 23.4&lt;br&gt;6, 3, 1.1&lt;br&gt;7, 3, 45.32&lt;br&gt;8, 3, 1.0&lt;br&gt;&lt;br&gt;Tabela R:&lt;br&gt;5, 5, 'Info'&lt;br&gt;
6, 6, 'Ala'&lt;br&gt;
7, 7, 'Info inne'&lt;br&gt;
8, 8, 'Kot'&lt;br&gt;&lt;br&gt;Pracuję na SQL Server 2005. :)&lt;br&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=570" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="SQL" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/SQL/default.aspx" /></entry><entry><title>C#: throw; vs throw e;</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/10/09/throw-vs-throw-e.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/10/09/throw-vs-throw-e.aspx</id><published>2007-10-09T20:10:00Z</published><updated>2007-10-09T20:10:00Z</updated><content type="html">Jakoś ostatnio często wpadają mi do rąk różne teksty o wyjątkach w
.Net. Z tego co da się zauważyć, to wyjątki, mimo swej prostej
koncepcji, nie są do końca rozumiane i dobrze używane. Na szczęście są
tacy ludzie jak &lt;a href="http://blogs.msdn.com/kcwalina/default.aspx"&gt;Krzysztof Cwalina&lt;/a&gt;, którzy pomagają biednym programistom. Bardzo przydatne informacje na temat wyjątków można znaleźć na jego &lt;a href="http://blogs.msdn.com/kcwalina/archive/2006/07/05/657268.aspx"&gt;blogu&lt;/a&gt;. Ale nie o tym chciałem pisać...&lt;br&gt;&lt;br&gt;Bardziej
zaawansowanych programistów C# może nie zaskoczę, ale na pewno znajdzie
się ktoś, kto nie do końca czuje różnicę (a może jej nie ma?).&lt;br&gt;&lt;br&gt;Jeżeli
złapiemy wyjątek w blok try-catch i mimo wszystko nie jesteśmy w stanie
go do końca obsłużyć to powinniśmy go puścić dalej. Początkujący
programista, aby ponownie zgłosić dalej ten sam wyjątek (ang. rethrow
exception), zapewne napisze tak (zgodnie ze swoją intuicją):&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; B()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;try&lt;/span&gt;&lt;br /&gt;    {&lt;br /&gt;        C();&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;catch&lt;/span&gt; (Exception e)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt; e;&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;CLR
w momencie, gdy zgłaszamy wyjątek pisząc &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;new&lt;/span&gt; Exception()&lt;/span&gt;&lt;/code&gt; zapisuje ślad stosu wywołań funkcji (ang. stack
trace) - można się do niego dostać za pomocą właściwości StackTrace.
Funkcja, w której wyjątek został zgłoszony jest jego źródłem. Niestety
ku zaskoczeniu wielu programistów (mojemu też, gdy się o tym
dowiedziałem) &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt; e;&lt;/span&gt;&lt;/code&gt;, co prawda rzuca ten
sam wyjątek, który został złapany, ale niestety ślad stosu zostaje
nadpisany nowym śladem ze źródłem w miejscu tego wywołania :(. To
nieuntuicyjne zachowanie jest przyczyną późniejszych problemów podczas
debugowania ponieważ nie znane jest rzeczywiste źródło błędu. Dlatego
należy pamiętać aby &lt;b&gt;nigdy nie rzucać wyjątków w ten sposób&lt;/b&gt;!&lt;br&gt;&lt;br&gt;Przykładowo rozważmy następujący program:&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; Main(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt;[] args)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; w &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; 1;&lt;br /&gt;    A();&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; A()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; w &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; 2;&lt;br /&gt;    B();&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; B()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;try&lt;/span&gt;&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; w &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; 3;&lt;br /&gt;        C();&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;catch&lt;/span&gt; (Exception e)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt; e;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; C()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; w &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; 4;&lt;br /&gt;    D();&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; D()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; w &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; 5;&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;new&lt;/span&gt; Exception(&lt;span style="color: #666666;background-color: #e4e4e4;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;"The method or operation is not implemented."&lt;/span&gt;);&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;W wyniku jego uruchomienia dostaniemy następującą informację:&lt;br&gt;&lt;br&gt;Wyjątek nieobsłużony: System.Exception: The method or operation is not implemented.&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.B() w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 30&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.A() w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 18&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.Main(String[] args) w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 12&lt;br&gt;&lt;br&gt;Rzeczywiście,
niepoprawne ponowne rzucenie wyjątku w metodzie B() skutkuje obcięciem
śladu stosu. Zatem jak sobie z tym poradzić? Do tego właśnie służy
użycie &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;;&lt;/span&gt;&lt;/code&gt; bez parametru. Jeżeli teraz
zmodyfikujemy odpowiednio metodę B() to sytuacja się odmieni.&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;void&lt;/span&gt; B()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;try&lt;/span&gt;&lt;br /&gt;    {&lt;br /&gt;        C();&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;catch&lt;/span&gt; (Exception e)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;;&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Wynik uruchomienia jest następujący:&lt;br&gt;&lt;br&gt;Wyjątek nieobsłużony: System.Exception: The method or operation is not implemented.&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.D() w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 43&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.C() w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 37&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.B() w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 30&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.A() w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 18&lt;br&gt;&amp;nbsp;&amp;nbsp; w ConsoleApplication1.Program.Main(String[] args) w D:\ConsoleApplication1\ConsoleApplication1\Program.cs:wiersz 12&lt;br&gt;&lt;br&gt;Widać
zatem, że ślad stosu nie został obcięty i łatwo odnaleźć prawdziwe
źródło błędu. Myślę, że nie trzeba nikogo zachęcać do takiego właśnie
obsługiwania wyjątków. To może nam tylko usprawnić pracę. Różnicę, o
której tu mowa, najlepiej widać na przykładzie kodu MSIL. Dla
pierwszego przypadku mamy:&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;L_000f: &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;ldloc.1&lt;/span&gt;&lt;br /&gt;L_0010: &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Natomiast dla drugiego:&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;L_000e: &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;nop&lt;/span&gt;&lt;br /&gt;L_000f: &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;rethrow&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Jak
łatwo zauważyć MSIL ma dwie osobne instrukcje. Jedna - throw - służy do
zgłaszania wyjątków a druga - rethrow - do ponownego zgłaszania
wyjątków. Twórcy C# skondensowali je jednak do jednego słowa
kluczowego, co czasami rodzi problemy.&lt;br&gt;&lt;br&gt;Chciałbym jeszcze
zauważyć jedną bardzo ciekawą rzecz. Co prawda wywołanie &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;;&lt;/span&gt;&lt;/code&gt; zachowuje ślad stosu to jednak ciekawy jest
sposób w jaki to robi. Otóż z powyższych rozważań można by było
wywnioskować, że powyższe wywołanie zachwuje się tak jakby wyjątek w
ogóle nie został złapany. Otóż nie! Wyjątek ten został złapany a co
więcej ma to swoje odbicie w śladzie stosu. Tak naprawdę to sam ślad
stosu jest obcinany a zachowywana jest tylko jego pełna tekstowa
reprezentacja (sic!). Zatem to co widzieliśmy w powyższych przykładach
jest tylko zapisem śladu stosu. Tę sytuację najlepiej zobaczyć
uruchamiając dwie powyższe aplikacje w trybie debug.&lt;br&gt;&lt;br&gt;Wyjątek po uruchomieniu pierwszej wersji:&lt;br&gt;&lt;img src="http://zine.net.pl/photos/posts_pictures/images/529/original.aspx"&gt;&lt;br&gt;&lt;br&gt;Wyjątek po uruchomieniu drugiej wersji:&lt;br&gt;&lt;img src="http://zine.net.pl/photos/posts_pictures/images/530/original.aspx"&gt;&lt;br&gt;&lt;br&gt;Na
załączonych obrazkach widać, że używając &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;;&lt;/span&gt;&lt;/code&gt; rzeczywiście zachowujemy ślad stosu, ale
gdy spojrzymy na okno rzeczywistego śladu stosu to okaże się, że kończy
się on na metodzie, która złapała i ponownie rzuciła ten sam wyjątek.
Zatem nie mamy możliwości pójścia w górę stosu i dotarcia do
rzeczywistego źródła wyjątku. Nie mamy też dostępu do żadnych
informacji kontekstowych takich jak zmienne i parametry tych wywołań,
które zostały ucięte.&lt;br&gt;&lt;br&gt;Można się zastanawiać jak poprawnie obsługiwać wyjątki. Ja myślę, że:&lt;br&gt;&lt;ul&gt;&lt;li&gt;Przede wszystkim należy używać &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;;&lt;/span&gt;&lt;/code&gt; zamiast &lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt; e;&lt;/span&gt;&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Po
drugie jeżeli nie obsługujemy wyjątku to nie ma sensu stosować bloku
try-catch i ponownie rzucać tego samego wyjątku bez żadnej obsługi.&lt;/li&gt;&lt;/ul&gt;Trzymając się tych wytycznych debugowanie będzie przyjemniejsze.&lt;br&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=531" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="CLR" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/CLR/default.aspx" /><category term="C#" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/C_2300_/default.aspx" /></entry><entry><title>Debugger i atrybuty, czyli jak usprawniać sobie codzienną pracę</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/10/06/Debugger-i-atrybuty_2C00_-czyli-jak-usprawniac-sobie-codzienna-prace.aspx" /><link rel="enclosure" type="application/x-zip-compressed" length="5582" href="http://zine.net.pl/blogs/nuwanda/attachment/513.ashx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/10/06/Debugger-i-atrybuty_2C00_-czyli-jak-usprawniac-sobie-codzienna-prace.aspx</id><published>2007-10-05T22:10:00Z</published><updated>2007-10-05T22:10:00Z</updated><content type="html">Każdy programista pewną część swojego życia spędza z... debuggerem ;)
Chyba zgodzicie się ze mną, że jest to bardzo przydatne narzędzie. Gdy
pojawia się jakiś błąd i nie do końca wiemy co w trawie piszczy to
odpalamy go i linia po lini weryfikujemy nasz program. Czasami jednak
debugowanie może być uciążliwe... Pokażę kilka atrybutów, którymi
możemy podpowiadać debuggerowi.&lt;br&gt;&lt;h3&gt;Zaglądanie do getterów, które podajemy jako parametry wywołania metody.&lt;/h3&gt;Najczęstszym
problemem, który napotykam podczas debugowania to denerwujące
wskakiwanie debuggera do getterów właściwości, które przekazuję w
wywołaniu metody. Zwykle taki getter nie ma żadnej dodatkowej logiki
tylko zwraca wartość prywatnego pola. Mając metodę z kilkoma tak
podanymi parametrami łatwo się zgubić. Wiele razy w pośpiechu wciskałem
F10, żeby wyskoczyć z tego gettera i okazywało się, że nacisnąłem o
jeden raz za dużo i ominąłem wywołanie metody (sic!). Żeby było
śmieszniej zwykle zdarza mi się to przy ostatnim parametrze metody! Na
szczęście jest na to ratunek. W &lt;b&gt;System.Diagnostics&lt;/b&gt; znajduje się atrybut &lt;b&gt;DebuggerStepThroughAttribute&lt;/b&gt;,
który zaaplikowany do metody lub właściwości karze debuggerowi ominąć
oznaczony element i zatrzymać się po jego wykonaniu :) Zatem naszą
znienawidzoną właściwość wystarczy udekorować:&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; _myProperty;&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; MyProperty&lt;br /&gt;{&lt;br /&gt;    [DebuggerStepThrough]&lt;br /&gt;    get { &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;return&lt;/span&gt; _myProperty; }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Tak oznaczonej właściwości już nie zobaczymy pod czas debugowania :D.&lt;br&gt;&lt;h3&gt;Przeglądanie zawartości złożonych obiektów&lt;/h3&gt;Czasami
budujemy złożone klasy. Mogą to być na przykład takie zawierające
wewnętrzne listy bądź też całkiem proste, z których budujemy większe
struktury (np. węzły drzewa). Debugując taką klasę mamy utrudnione
zadanie ponieważ musimy rozwijać różne elementy, żeby w końcu zobaczyć
co zawiera ta wewnętrzna lista, albo zaglądać do elementów wgłąb tak
jak to ma miejsce przy debugowaniu drzew. Przykładowe debugowanie
drzewa binarnego wygląda tak:&lt;br&gt;&lt;br&gt;&lt;img src="http://zine.net.pl/photos/posts_pictures/images/512/original.aspx"&gt;&lt;br&gt;&lt;br&gt;Jak
widać na załączonym obrazku wyciągnięcie wartości, które nas
rzeczywiście interesują, czyli wartości w poszczególnych węzłach, jest
trudne. Obraz jest zaciemniony przez inne pola. Istnieje jednak sposób,
aby ułatwić sobie życie - atrybut &lt;b&gt;DebuggerTypeProxyAttribute&lt;/b&gt;.
Za jego pomocą możemy określić proxy, które będzie wyświetlanie podczas
debugowania. Klasę proxy definiuje się bardzo łatwo. Może to być
dowolna klasa, która posiada konstruktor przyjmujący parametr typu
klasy, którą opatrzymy tym atrybutem. Podczas debugowania to klasa
proxy będzie wyświetlana, czyli zobaczymy to, co w niej zdefiniujemy. W
przykładzie z drzewem zdefiniowałem sobie taką klasę i dodałem do niej
publiczne pole &lt;b&gt;InOrder&lt;/b&gt;, które zwraca wartości drzewa właśnie w takim porządku:&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt;[] InOrder&lt;br /&gt;{&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;        List&amp;lt;String&amp;gt; result &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;new&lt;/span&gt; List&amp;lt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt;&amp;gt;();&lt;br /&gt;        GetInOrder(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;this&lt;/span&gt;._node, result, &lt;span style="color: #666666;background-color: #e4e4e4;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;""&lt;/span&gt;);&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;return&lt;/span&gt; result.ToArray();&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Należy tutaj zauważyć kolejny pomocny atrybut. &lt;b&gt;DebuggerBrowsableAtribute &lt;/b&gt;służy do podpowiadania debuggerowi jak wyświetlać dane pole. Do dyspozycji mamy trzy wartości: &lt;b&gt;Collapsed &lt;/b&gt;- element będzie zwinięty, &lt;b&gt;Never &lt;/b&gt;- element nie będzie pokazywany oraz &lt;b&gt;RootHidden &lt;/b&gt;-
element nie będzie pokazywany, ale zamiast niego zostaną wstawione jego
podelementy. W powyższym przykładzie ukryłem element InOrder, aby od
razu wyświetlały się elementy listy, którą zwraca. W taki oto sposób
debugowanie stało się o wiele prostsze:&lt;br&gt;&lt;br&gt;&lt;img src="http://zine.net.pl/photos/posts_pictures/images/511/original.aspx"&gt;&lt;br&gt;&lt;br&gt;Jak nie trudno zauważyć zawartość drzewa jest banalna do odczytania.&lt;br&gt;&lt;h3&gt;Wyświetlanie złożonych obiektów&lt;/h3&gt;Ostatnim atrybutem, o którym chciałem jeszcze wspomnieć jest &lt;b&gt;DebuggerDisplayAttribute&lt;/b&gt;,
który służy do definiowania napisu, który będzie wyświetlany dla
złożonego elementu (klasy). Aplikując go podajemy również napis, który
będzie wyświetlany. W tym napisie możemy odwoływać się do właściwości
obiektu poprzez {Property}:&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;[DebuggerDisplay(&lt;span style="color: #666666;background-color: #e4e4e4;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;"Val: {Value}, Desc: {Description}"&lt;/span&gt;)]&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;class&lt;/span&gt; SimpleClass&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; _value;&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; Value {...}&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;private&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; _description;&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; Description {...}&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; SimpleClass(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; value, &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; description) {...}&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Dzięki niemu nasza klasa będzie widoczna w debuggerze w następujący sposób:&lt;br&gt;&lt;br&gt;&lt;img src="http://zine.net.pl/photos/posts_pictures/images/510/original.aspx"&gt;&lt;br&gt;&lt;br&gt;Przedstawiłem tu techniki, z których korzystam na co dzień. Może i wam się przydadzą. :)&lt;br&gt;W załączniku zamieściłem przykładowy program dla ciekawskich.&lt;br&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=513" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Visual Studio" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Visual+Studio/default.aspx" /><category term="Tips&amp;amp;Tricks" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Tips_2600_amp_3B00_Tricks/default.aspx" /><category term="Kod źr&amp;#243;dłowy" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Kod+_7A01_r_26002300_243_3B00_d_4201_owy/default.aspx" /><category term="Debugging" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Debugging/default.aspx" /></entry><entry><title>Jak wyświetlić okno aby okno obecnie posiadające focus nie straciło go?</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/10/04/Jak-wyswietlic-okno-aby-okno-obecnie-posiadajace-focus-nie-stracilo-go.aspx" /><link rel="enclosure" type="application/x-zip-compressed" length="14328" href="http://zine.net.pl/blogs/nuwanda/attachment/499.ashx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/10/04/Jak-wyswietlic-okno-aby-okno-obecnie-posiadajace-focus-nie-stracilo-go.aspx</id><published>2007-10-04T08:43:00Z</published><updated>2007-10-04T08:43:00Z</updated><content type="html">Zadanie (Windows Forms):&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;div align="center"&gt;&lt;b&gt;Z pierwszego okna otwórz drugie okno, które ma się pojawić nad pierwszym oknem, ale tak, żeby focus pozostał w pierwszym oknie&lt;/b&gt;.&lt;br&gt;&lt;/div&gt;&lt;br&gt;Ostatnio
spotkałem się właśnie z takim problemem. Chciałem zaprogramować coś
a'la ToolTip, który będzie mi się pokazywał nad moim polem tekstowym i
w którym będę mógł umieszczać swoje informacje. Okazało się, że sprawa
nie jest tak oczywista jak mogłoby się wydawać. Oczywiście zacząłem od
ustawienia drugiej formatki jako &lt;b&gt;TopMost&lt;/b&gt;, żeby zawsze pojawiała
się na wierzchu. Największy problem polegał na zabraniu (albo najlepiej
nie daniu) otwieranemu oknu focusa.&lt;br&gt;&lt;br&gt;&lt;b&gt;Pierwsze znalezisko - ale niestety nie rozwiązuje problemu do końca.&lt;/b&gt;&lt;br&gt;Dowiedziałem się, że implementując formatkę (dziedzicząc po Form) można nadpisać właściwość &lt;b&gt;ShowWithoutActivation&lt;/b&gt;.&lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;protected&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;override&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;bool&lt;/span&gt; ShowWithoutActivation&lt;br /&gt;{&lt;br /&gt;    get { &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;return&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;true&lt;/span&gt;; }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Jeżeli to zrobmy i zwrócimy &lt;b&gt;true&lt;/b&gt; to okno będzie otwierało się &lt;u&gt;bez focusa&lt;/u&gt;. Fajnie, już myślałem, że sprawa załatwiona. Niestety okazuje się, że opcja ta nie działa jeżeli okno ma ustawioną właściwość &lt;b&gt;TopMost &lt;/b&gt;na &lt;b&gt;true &lt;/b&gt;:(. W komentarzu na &lt;a href="http://msdn2.microsoft.com/en-us/library/system.windows.forms.form.showwithoutactivation.aspx"&gt;MSDN&lt;/a&gt; znalazłem, że aby podobny efekt uzyskać z oknem &lt;b&gt;TopMost &lt;/b&gt;to należy skorzystać bezpośrednio z API Windowsa.&lt;br&gt;&lt;br&gt;&lt;b&gt;Druga wersja - tym razem rozwiązująca zadanie.&lt;/b&gt;&lt;br&gt;Po kolejnych kulku minutach na Googlach znalazłem to czego szukałem. &lt;br&gt;&lt;br&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;[DllImport(&lt;span style="color: #666666;background-color: #e4e4e4;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;"User32.dll"&lt;/span&gt;)]&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;extern&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;int&lt;/span&gt; ShowWindow(System.IntPtr hWnd, &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;short&lt;/span&gt; cmdShow);&lt;/span&gt;&lt;/code&gt;&lt;br&gt;&lt;br&gt;Wywołanie
tej metody z uchwytem do pokazywanego okna i z parametrem cmdShow = 4
(SW_SHOWNOACTIVATE) załatwia sprawę. Okienko ładnie się pokazuje i nie
dostaje focusa. Great! :)&lt;br&gt;&lt;br&gt;W załączniku umieściłem przykładową
aplikację, która pokazuje omówione tu techniki. Symuluje ona edycję
tekstu w polu tekstowym oraz wyświetlanie okien pomocniczych. Wciskając
kombinacje klawiszy CTRL + [Z, X, C] otwierać będą się kolejno: okno z &lt;b&gt;TopMost = true &lt;/b&gt;przy użyciu standardowej metody &lt;b&gt;Show&lt;/b&gt;, okno z ustawionym &lt;b&gt;TopMost = true&lt;/b&gt;, ale za pomocą metody &lt;b&gt;ShowWindow &lt;/b&gt;oraz okno z &lt;b&gt;TopMost = false&lt;/b&gt; z nadpisaną właściwością  &lt;b&gt;ShowWithoutActivation &lt;/b&gt;zwracającą &lt;b&gt;true&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Jeżeli jesteście ciekawi to zerknijcie sobie.&lt;br&gt;&lt;img src="http://zine.net.pl/aggbug.aspx?PostID=499" width="1" height="1"&gt;</content><author><name>nuwanda</name><uri>http://zine.net.pl/members/nuwanda.aspx</uri></author><category term="Windows Forms" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Windows+Forms/default.aspx" /><category term="Tips&amp;amp;Tricks" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Tips_2600_amp_3B00_Tricks/default.aspx" /><category term="Kod źr&amp;#243;dłowy" scheme="http://zine.net.pl/blogs/nuwanda/archive/tags/Kod+_7A01_r_26002300_243_3B00_d_4201_owy/default.aspx" /></entry><entry><title>Konkurs Enterprise Library – Exception Handling (EHAB)</title><link rel="alternate" type="text/html" href="http://zine.net.pl/blogs/nuwanda/archive/2007/10/01/konkurs-enreprise-library-exception-handling-ehab.aspx" /><id>http://zine.net.pl/blogs/nuwanda/archive/2007/10/01/konkurs-enreprise-library-exception-handling-ehab.aspx</id><published>2007-10-01T11:32:00Z</published><updated>2007-10-01T11:32:00Z</updated><content type="html">&lt;P&gt;W zeszłym tygodniu mieliście okazję przeczytać o &lt;A href="http://zine.net.pl/blogs/arkadiusz_wasniewski/archive/2007/09/24/konkurs-enterprise-library-logging-lab.aspx"&gt;Logging Applicatinn Block&lt;/A&gt; i poznaliście pierwsze zadanie konkursowe. Dziś kontynuacja konkursu!&lt;BR&gt;&lt;/P&gt;
&lt;H3&gt;&lt;B&gt;Refaktoryzacja do Exception Handling Application Block&lt;/B&gt;&lt;/H3&gt;W każdej aplikacji, małej i dużej, mogą pojawić się sytuacje wyjątkowe. Prosta operacja dostępu do pliku może się zakończyć kilkoma różnymi problemami. Wyjątki od samego początku tworzenia aplikacji towarzyszą każdemu programiście. W prostych aplikacjach, nie posiadających struktury warstwowej, zarządzanie wyjątkami nie sprawia wielu problemów. Jeżeli jednak mamy do czynienia ze skomplikowanym systemem o strukturze warstwowej, być może rozproszonym, oraz dużą ilością kodu źródłowego to może okazać się, że zadanie to nie jest trywialne. Aplikacja taka będzie wykonywała wiele różnych operacji, które mogą się zakończyć sytuacjami wyjątkowymi. Operacje te będą wykonywane w różnych warstwach systemu. Nie jest też powiedziane, że w każdej warstwie wyjątki mają być traktowane tak samo. &lt;BR&gt;&lt;BR&gt;Rozwiązania dla tych wszystkich przypadków dostarcza nam &lt;B&gt;Exception Handling Application Block&lt;/B&gt;.&lt;BR&gt;
&lt;H3&gt;Uzasadnienie&lt;/H3&gt;EHAB to jeden z komponentów potężnej biblioteki znanej pod nazwą Enterprise Library. Jest przeznaczony do spójnej i kompleksowej obsługi sytuacji wyjątkowych w dowolnym systemie. Za jego pomocą można obsługiwać wyjątki we wszystkich warstwach architektonicznych aplikacji. Tak jak i inne bloki tak i EHAB posiada jeden scentralizowany punkt (plik konfiguracyjny), w którym można definiować strategie przetwarzania wyjątków dla całej aplikacji, bez konieczności jej ponownej kompilacji. &lt;BR&gt;&lt;BR&gt;Definiowanie strategii obsługi wyjątku jest bardzo proste. Podstawowym elementem konstrukcyjnym są tak zwane exception handlers. Wraz z biblioteką dostarczono kilka z nich, które są najczęściej wykorzystywane podczas obsługi błędów:&lt;BR&gt;
&lt;UL&gt;
&lt;LI&gt;Wrap Handler – pozwala na opakowanie obsługiwanego wyjątku w wyjątek innego typu. Oryginalny wyjątek jest zachowywany w polu&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; InnerException nowego wyjątku.
&lt;LI&gt;Replace Handler – zastępuje obsługiwany wyjątek nowym wyjątkiem.
&lt;LI&gt;Logging Handler – pozwala na zapisanie wszystkich informacji dotyczących wyjątku.
&lt;LI&gt;Fault Contract Exception Handler – jest przeznaczony do obsługi wyjątków w WCF.&lt;/LI&gt;&lt;/UL&gt;Definiowanie zaczynamy od określenia co najmniej jednej wytycznej (ang. policy) dotyczącej obsługi konkretnych błędów. Zestaw wytycznych definiuje się w zależności od potrzeb. Dla danej wytycznej definiujemy zbiór typów wyjątków, dla których chcemy określić sposób ich obsługi. Następnie dla każdego typu wyjątku definiujemy zestaw (ciąg) exception handlers, które będą wykorzystywane do obsługi danego wyjątku. Należy tu wspomnieć, że exception handlers są łączone w ciąg i wykonywane jeden po drugim, a wynikiem obsługi wyjątku jest wynik uruchomienia ostatniego handlera.&lt;BR&gt;&lt;BR&gt;Wywołania EHAB mają zastąpić typowy kod, który pojawia się w blokach catch. Dzięki temu programiści nie muszą powtarzać tego samego kodu (np. logowania informacji o wyjątku) w wielu miejscach systemu. EHAB dostarcza im spójnego mechanizmu pozwalającego na łatwą konfigurację. Przykładowe użycie wygląda następująco:&lt;BR&gt;&lt;BR&gt;Kod. 1 – obsługa wyjątku za pomocą EHAB&lt;BR&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;try&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;// Run code.&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;catch&lt;/span&gt;(Exception ex)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;bool&lt;/span&gt; rethrow &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; ExceptionPolicy.HandleException(ex, &lt;span style="color: #666666;background-color: #e4e4e4;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;" Data Access Policy"&lt;/span&gt;);&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;if&lt;/span&gt; (rethrow)&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;;&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;BR&gt;&lt;BR&gt;Jak łatwo zauważyć, programista w trakcie tworzenia funkcji nie musi rozróżniać typów wyjątków, które może zgłosić wywoływany fragment kodu. Wystarczy, że wykorzysta pojedynczą klauzulę catch i w niej przekaże obsługę błędu do EHAB, a ten na podstawie konfiguracji odpowiednio obsłuży podany wyjątek.&lt;BR&gt;
&lt;H3&gt;Sposób wykonania&lt;/H3&gt;Refaktoryzacja do EHAB jest w moim odczuciu dość prosta i mechaniczna. W systemie, który będziemy poddawać refaktoryzacji znajdziemy wiele bloków try catch. To one będą głównym punktem naszego zainteresowania. Wprowadzanie EHAB wygląda następująco:&lt;BR&gt;
&lt;OL&gt;
&lt;LI&gt;Dodaj odpowiednie referencje do projektu oraz przygotuj plik konfiguracyjny.
&lt;LI&gt;Zlokalizuj wszystkie miejsca, w których obsługiwane są wyjątki.
&lt;LI&gt;Dla każdego z tych punktów:
&lt;OL&gt;
&lt;LI&gt;Jeżeli potrzeba wprowadź nowe typy wyjątków.
&lt;LI&gt;Przenieś definicję obsługi wyjątku do pliku konfiguracyjnego do odpowiedniej wytycznej (policy).
&lt;LI&gt;Zastąp wszystkie klauzule catch jedną ogólną klauzulą (kod 1) oraz określ wytyczną, z której należy skorzystać.&lt;/LI&gt;&lt;/OL&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;H3&gt;Przykład&lt;/H3&gt;Przykładowa aplikacja jest symulacją prostego okna logowania użytkownika do systemu. Użytkownik musi wprowadzić swój identyfikator oraz hasło, a następnie wcisnąć przycisk OK. Budowa aplikacji symuluje podział trójwarstwowy: warstwę dostępu do bazy danych, warstwę logiki biznesowej oraz interfejs użytkownika. Kod obsługi wyjątków znajduje się w każdej warstwie, a w warstwie UI, wyjątki zamieniane są na komunikaty dla użytkownika. Należy zauważyć, że wyjątek pojawiający się w warstwie dostępu do bazy danych propagowany jest w niezmienionej formie aż do interfejsu użytkownika.&lt;BR&gt;&lt;BR&gt;Takie podejście do obsługi sytuacji wyjątkowych rodzi kilka problemów. Przede wszystkim wyjątki zawierają bardzo dużo informacji o systemie łącznie z np. konfiguracją połączenia z bazą danych, która może zawierać nazwę użytkownika oraz hasło. Zwykły użytkownik aplikacji nie potrzebuje aż tak szczegółowych danych, a w przypadku nazwy użytkownika i hasła dostępu do bazy danych nie powinien mieć do nich dostępu! Takich informacji może potrzebować programista lub osoba utrzymująca system. Dlatego właśnie należy ukrywać takie dane.&lt;BR&gt;&lt;BR&gt;Dobrą praktyką jest też przekazywanie wyjątków tylko bezpośrednio do warstwy nadrzędnej, a nie jak to jest zrobione w przykładowej aplikacji z warstwy dostępu do bazy danych prosto do interfejsu użytkownika. Takie podejście pozwala odseparować tworzenie oraz zarządzanie poszczególnymi warstwami. Te dwa problemy rozwiążemy wprowadzając EHAB.&lt;BR&gt;
&lt;H3&gt;Zdefiniowanie konfiguracji dla EHAB&lt;/H3&gt;W pierwszym kroku refaktoryzacji przygotujemy projekt do wprowadzenia EHAB. Najpierw dodamy referencje do wymaganych bibliotek. Pierwszą z nich jest &lt;B&gt;Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll&lt;/B&gt;, która jest biblioteką podstawową EHAB i zawiera całą jego&amp;nbsp; implementację. Druga biblioteka to &lt;B&gt;Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.dll&lt;/B&gt;, która zawiera implementację exception handlers pozwalających zapisywać szczegółowe dane o wyjątkach za pomocą LAB.&lt;BR&gt;&lt;BR&gt;W drugim kroku zainicjujemy plik konfiguracyjny aplikacji. Klikamy na nim prawym przyciskiem i wybieramy ‘Edit Enterprise Library Configuration’. Otworzy nam się drzewo konfiguracyjne całego EntLiba. Naszym zadaniem jest dodanie bloku konfiguracyjnego dla EHAB (rys. 1) i jego wstępna konfiguracja.&lt;BR&gt;&lt;BR&gt;&lt;IMG src="http://zine.net.pl/photos/posts_pictures/images/484/original.aspx"&gt;&lt;BR&gt;Rys.1 Dodawanie konfiguracji dla EHAB.&lt;BR&gt;&lt;BR&gt;Następnie musimy zdefiniować co najmniej jedną wytyczną (policy). My zdefiniujemy sobie dwie zgodnie z naszym podziałem na warstwy. Będziemy mieli wytyczną dotyczącą wyjątków z warstwy dostępu do bazy danych oraz drugą wytyczną dla logiki biznesowej (rys. 2).&lt;BR&gt;&lt;BR&gt;&lt;IMG src="http://zine.net.pl/photos/posts_pictures/images/485/original.aspx"&gt;&lt;BR&gt;Rys. 2 Wytyczne dla warstwy dostępu do bazy danych oraz logiki biznesowej&lt;BR&gt;
&lt;H3&gt;Wprowadzenie EHAB&lt;/H3&gt;Refaktoryzację zaczniemy od warstwy najniższej, czyli od dostępu do bazy danych. Aby odseparować tą warstwę do warstwy logiki biznesowej wprowadzimy własne typy wyjątków i będziemy je propagować w górę. W przykładowej aplikacji mamy blok catch, który obsługuje wyjątki typu SqlException.&lt;BR&gt;&lt;BR&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;class&lt;/span&gt; UserIdentityDAO&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; UserIdentity GetUserIdentity(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; identifier)&lt;br /&gt;    {&lt;br /&gt;        UserIdentity identity &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;null&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;try&lt;/span&gt;&lt;br /&gt;        {&lt;br /&gt;            SqlConnection conn &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;new&lt;/span&gt; SqlConnection(Properties.Settings.Default.UserDatabaseConnectionString);&lt;br /&gt;            conn.Open();&lt;br /&gt;            &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;//Kontynuacja...&lt;/span&gt;&lt;br /&gt;        }&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;catch&lt;/span&gt; (SqlException)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;//Robimy co potrzebujemy, na przykład &lt;/span&gt;&lt;br /&gt;            &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;//zapisujemy info o błędzie&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt;;&lt;br /&gt;        }&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;return&lt;/span&gt; identity;&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;BR&gt;&lt;BR&gt;Najpierw zdefiniujemy w tej warstwie nowy typ wyjątku, który odpowiadał będzie błędom dostępu do bazy danych. Nazwijmy go DatabaseException. Definiując nowe wyjątki warto kierować się pewnymi dobrymi praktykami. Ponieważ konstruktory nie są dziedziczone z klasy bazowej należy dodać je ręcznie. Zaleca się definowanie trzech konsruktorów tak, jak w kodzie poniżej. Dodatkowo jeżeli wyjątek ma być przekazywany w wywołaniach zdalnych (remoting) to powinien być oznaczony atrybutem SerializableAttribute i implementować interfejs ISerializable.&lt;BR&gt;&lt;BR&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;class&lt;/span&gt; DatabaseException : Exception&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; DatabaseException() : &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;base&lt;/span&gt;() { }&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; DatabaseException(String message) : &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;base&lt;/span&gt;(message) { }&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; DatabaseException(String message, Exception innerException) : &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;base&lt;/span&gt;(message, innerException) { }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;BR&gt;&lt;BR&gt;Wyjątek ten chcemy propagować do warstwy logiki biznesowej za każdym razem, gdy wystąpi błąd dostępu do bazy danych czyli w przypadku wystąpienia wyjątku SqlException. Przekształcamy odpowiednio kod aplikacji: zmieniamy kod obsługi błędu w warstwie dostępu do bazy danych oraz zmieniamy typ wyjątku przechwytywany w wyższych warstwach.&lt;BR&gt;&lt;BR&gt;&lt;code&gt;&lt;span style="color: Black;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;class&lt;/span&gt; UserIdentityDAO&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;public&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;static&lt;/span&gt; UserIdentity GetUserIdentity(&lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;string&lt;/span&gt; identifier)&lt;br /&gt;    {&lt;br /&gt;        UserIdentity identity &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;null&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;try&lt;/span&gt;&lt;br /&gt;        {&lt;br /&gt;            SqlConnection conn &lt;span style="color: Red;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;=&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;new&lt;/span&gt; SqlConnection(Properties.Settings.Default.UserDatabaseConnectionString);&lt;br /&gt;            conn.Open();&lt;br /&gt;            &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;//Kontynuacja...        }&lt;/span&gt;&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;catch&lt;/span&gt; (SqlException se)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;//Robimy co potrzebujemy, na przykład &lt;/span&gt;&lt;br /&gt;            &lt;span style="color: Green;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;//zapisujemy info o błędzie&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;throw&lt;/span&gt; &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;new&lt;/span&gt; DatabaseException(&lt;span style="color: #666666;background-color: #e4e4e4;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;"Błąd dostępu do bazy danych."&lt;/span&gt;, se);&lt;br /&gt;        }&lt;br /&gt;        &lt;span style="color: Blue;background-color: Transparent;font-family: Courier New;font-size: 11px;font-weight: normal;"&gt;return&lt;/span&gt; identity;&lt;br /&gt;    }&lt;br /&gt;}&lt;/span&gt;&lt;/code&gt;&lt;BR&gt;&lt;BR&gt;Należy zauważyć, że tworząc nowy wyjątek chcemy zachować także ten oryginalny. Dlatego też umieszczamy ten oryginalny jako wyjątek wewnętrzny (inner exception) nowego wyjątku. Dzięki temu nie stracimy cennych (z punktu widzenia programisty) informacji, które są na wagę złota w przypadku p