Rozdział 15.
Zastosowanie obiektów biznesowych
W tym rozdziale zajmiemy się nowym obszarem zastosowań ASP.NET. W dwóch poprzednich częściach książki przedstawione zostały podstawowe informacje na temat tej technologii oraz mechanizmy dostępu do danych. Teraz zajmiemy się zagadnieniami zaawansowanymi, które pozwolą Ci na tworzenie pełnych aplikacji ASP.NET.
W tym rozdziale poznasz obiekty biznesowe — czyli komponenty których będziesz mógł używać w swoich aplikacjach ASP.NET. Różnica pomiędzy obiektami biznesowymi a komponentami których używaliśmy do tej pory (takimi jak obiekty ADO.NET oraz obiekty do obsługi danych XML) polega na tym, iż obiekty biznesowe będziemy w całości tworzyli własnoręcznie. W tym rozdziale dowiesz się do czego one służą oraz jak należy je tworzyć i używać.
W tym rozdziale zostaną omówione następujące zagadnienia:
Czym są komponenty i obiekty biznesowe.
W jaki sposób ASP.NET korzysta z komponentów.
Jak można używać komponentów.
Jak tworzyć i używać obiektów biznesowych.
W jaki sposób można korzystać z komponentów, które nie zostały utworzone w środowisku .NET.
Prezentacja komponentów
Komponenty to obiekty, które mogą być wielokrotnie wykorzystywane w wielu różnych aplikacjach. Zazwyczaj odwzorowują one obiekty spotykane w realnym świecie. Wykorzystajmy jeszcze raz przykład z fabryką zegarów, przedstawiony w rozdziale 3, pt.: „Stosowanie Visual Basic.NET”. Aby zbudować zegar należy przygotować odpowiednie komponenty, takie jak sprężyny, koła zębate, szkło, drewno oraz wahadło i złożyć je w jedną całość. W bardzo podobny sposób są tworzone aplikacje ASP.NET oraz zwyczajne aplikacje. Składają się one z wielu części, które po złożeniu tworzą jedną spójną całość.
Wyobraź sobie, że tworzone przez Ciebie zegary składają się tylko z jednego komponentu — każdy zegar jest jednym kawałkiem drzewa. Takie zegary nie będą działać najlepiej — wskazówki nie będą się kręcić, a wahadło — poruszać. Jednak jeśli podzielisz zegar na oddzielne komponenty — wskazówkę godzinową, minutową i sekundową, wahadło oraz tarczę — znacznie łatwiej będzie Ci stworzyć zegar działający zgodnie z oczekiwaniami. Co więcej, jeśli przy budowie zegara wykorzystasz wiele różnych części, znacznie łatwiej będzie je wymieniać. Na przykład, jeśli wskazówka minutowa się złamie, to bez problemu będziesz mógł ją wymontować i zastąpić inną. Lub jeśli zostanie opracowane bardziej zaawansowane wahadło, będziesz mógł je wykorzystać w swoim zegarze oszczędzając tym samym zarówno czasu jak i pieniędzy.
Jest jednak jeden argument przemawiający przeciwko wykorzystywaniu komponentów. Otóż wykorzystanie zbyt wielu komponentów może przekreślić wszelkie korzyści poprzez zbytnie skomplikowanie aplikacji. Dlaczego niby miałbyś dzielić na części wahadło? Części działające dobrze jako jedna całość nie należy niepotrzebnie dzielić.
W kontekście ASP.NET komponenty są fragmentami kodu, który można wielokrotnie wykorzystywać w celu rozszerzenia możliwości aplikacji lub wzbogacenia jej o nowe funkcje. Elementy sterujące użytkownika o których pisałem w rozdziale 5. są właśnie przykładem komponentów. Także kod obsługi formularzy można traktować jako komponent. Nawet internetowe elementy sterujące są komponentami, których można używać przy tworzeniu stron ASP.NET. Komponenty są wykorzystywane w celu opisania istniejących obiektów, takich jak kalendarz bądź książka. W przypadku storn ASP.NET przykładami komponentów mogą być pola tekstowe bądź zbiory wyników pobrane z bazy danych.
Czym są obiekty biznesowe?
Nowe określenie
Obiekty biznesowe są komponentami zawierającymi kod wykorzystywany w tworzonej aplikacji. Kod udostępniający możliwości funkcjonalne niezwiązane z obsługą interfejsu użytkownika jest nazywany logiką biznesową lub regułami biznesowymi. A zatem, komponenty implementujące logikę biznesową są nazywane obiektami biznesowymi.
--> Na przykład, jeśli byś pisał aplikację prezentującą informacje pobierane z bazy danych, to cały kod związany z obsługą bazy i stanowiący warstwę pośrednią pomiędzy bazą a interfejsem użytkownika byłby logiką biznesową[Author:p8R] . W optymalnym przypadku, kod stanowiący logikę biznesową powinien być oddzielony od strony ASP.NET i zaimplementowany w formie obiektu biznesowego. Strony ASP.NET powinny być wykorzystywane wyłącznie jako interfejs użytkownika i realizować przetwarzanie związane z obsługą „klienckiej” części aplikacji.
Bardzo popularny przykład obiektów biznesowych można spotkać na witrynach zajmujących się handlem elektronicznym, które muszą pobierać informacje o kosztach wysyłki od różnych firm przewozowych. Programista mógłby zaimplementować tę logikę w formie strony ASP.NET, lecz w takim przypadku, modyfikacja tej logiki w razie zmiany sposobu obliczania kosztów wysyłki byłaby znacznie utrudniona (nie wspominając w ogóle, o znacznie ograniczonych możliwościach wielokrotnego wykorzystania takiego kodu). Sprytniejszy programista mógłby natomiast stworzyć obiekt obliczający koszty wysyłki towarów, który można by wykorzystywać w dowolnych aplikacjach ASP.NET. Komponent ten pobierałby informacje o kosztach wysyłki z bazy danych firmy przewozowej i dostarczał aplikacji internetowej wszelkich koniecznych informacji. Komponent taki można by wielokrotnie wykorzystywać, a jakiekolwiek zmiany logiki biznesowej musiałyby być wprowadzane tylko w jednym miejscu, bez konieczności modyfikowania stron ASP.NET.
Dlaczego warto używać komponentów?
Być może słyszałeś o trójwarstwowym modelu aplikacji, w którym aplikacje są dzielone na trzy (czasami niezbyt rozdzielne) warstwy —prezentacji lub interfejsu użytkownika, logiki biznesowej oraz danych. Model ten z powodzeniem można wykorzystać przy tworzeniu aplikacji internetowych, a jego implementacja nie nastręcza zbyt wielu problemów. Trójwarstwowy model aplikacji został przedstawiony na rysunku 15.1.
Rysunek 15.1. Trójwarstwowy model aplikacji składa się z warstwy interfejsu użytkownika, logiki biznesowej oraz danych
Opis rysunku
UI layer — Warstwa interfejsu użytkownika
ASP.NET pages…— Strony ASP.NET, elementy sterujące użytkownika, itp.
Bisiness objects — Warstwa obiektów biznesowych
Business rules … — Reguły biznesowe (logika biznesowa), pomocnicze możliwości funkcjonalne, itp.
Data layer — Warstwa danych
Database … — Baza danych, procedury zachowane, itp.
Trójwarstwowy model aplikacji przypomina nieco produkcję teatralną. Pierwszą warstwą są aktorzy występujący na scenie. Stanowią oni „interfejs użytkownika” dla widzów siedzących na widowni, przyciągając ich uwagę i dostarczając wrażeń. Z tą warstwą „aplikacji” mają kontakt widzowie oglądający przedstawienie.
Drugą warstwę stanowią osoby odpowiedzialne za produkcję przedstawienia i pomoc przy jego realizacji — orkiestra, osoby obsługujące scenografię, itp. Wszystkie te osoby prowadzą interakcję z aktorami występującymi na scenie, jednak pozostają niewidoczni dla publiczności. Osoby te kierują wykonaniem przedstawienia i dzięki nim aktorzy mogą odtwarzać swoje role.
I w końcu trzecią warstwę stanowią osoby odpowiedzialne za materia i scenografię — pisarze, artyści, scenografowie, i tak dalej. Wszystkie te osoby pracują wspólnie, aby nadać produkcji znaczenie. Publiczność nigdy ich nie widzi, dostrzega jedynie efekty ich pracy.
Ten model realizacji przedstawień jest doskonale zdefiniowany i dostosowany do konkretnych potrzeb. Wyobraź sobie co by się stało gdyby zabrakło jednej z jego „warstw”. Bez aktorów, w ogóle nie można by wystawić żadnego przedstawienia. Bez pisarzy i artystów nie byłoby czego wystawiać. Natomiast bez warstwy pośredniej — na przykład scenografów — aktorzy mieliby bardzo duże trudności z wykonywaniem swej pracy, a osoby zaliczające się do innej „warstwy” musiałyby zająć ich miejsce.
Ten sam model można zastosować przy tworzeniu aplikacji internetowych. Pominięcie jednej z warstw sprawa, że stworzenie aplikacji staje się o wiele trudniejsze. W przypadku witryn zajmujących się handlem elektronicznym, pierwszą warstwą jest interfejs użytkownika — formularze, „koszyki”, obrazy, itp. Pośrednia warstwa logiki biznesowej składa się z mechanizmów określających ceny towarów, koszty wysyłki, itd. I w końcu trzecia warstwa — warstwa danych — składa się z listy towarów przechowywanych w bazie danych. Jeśli którejkolwiek z tych warstw zabraknie, inna będzie musiała przejść jej funkcje.
Rozpatrując całe zagadnienie bardziej konkretnie, wykorzystanie obiektów biznesowych jako warstwy pośredniej pozwala na lepszą separację kodu oraz lepsze zdefiniowanie aplikacji. Dzięki nim, strony ASP.NET nie muszą już zawierać tajemniczego, długiego kodu, który w żaden sposób nie jest związany z interfejsem użytkownika. Przeznaczeniem tych stron jest wizualne przedstawianie informacji i przyciągnięcie uwagi użytkownika. Po co zatem umieszczać w nich kod, który z wizualną prezentacją informacji nie ma niczego wspólnego?
W porządku, właśnie tym zajmowaliśmy się w kilku ostatnich rozdziałach książki. Możliwości funkcjonalne, takie jak mechanizmy zapewniające dostęp do baz danych można umieścić w obiektach biznesowych zaliczanych do środkowej warstwy aplikacji. Jednak w wielu spośród przedstawianych przykładów, wykorzystywane możliwości funkcjonalne były tak proste, iż nie trzeba było implementować ich jako trzeciej warstwy i niepotrzebnie komplikować konstrukcji całej aplikacji. Obiekty biznesowe doskonale nadają się implementacji możliwości funkcjonalnych, które nie mają niczego wspólnego z interfejsem użytkownika. Niemniej jednak to Ty jako programista, musisz określić czy aplikacja jest na tyle skomplikowana, aby warto było wprowadzać do niej trzecią warstwę. Komponenty zapewniają także znacznie efektywniejszy sposób wykorzystania możliwości funkcjonalnych. Na przykład, przypomnij sobie kalendarz, przedstawiony w rozdziale 5, pt.: „Podstawowe wiadomości o tworzeniu formularzy internetowych”. Przy użyciu zaledwie kilku wierszy kodu, pozwala on na wyświetlenie w pełni funkcjonalnego kalendarza dostosowanego do wyglądu tworzonej aplikacji. Twórca strony nie musi się przejmować sposobem generacji kalendarza, wyświetlaniem poszczególnych tygodni, określaniem ilości dni w miesiącu, itp. Wszystkie te czynności są wykonywane za nas.
Komponenty należy tworzyć właśnie w taki sposób — tak, aby używający ich programiści nie musieli zaprzątać sobie głowy niewidocznymi sposobami działania komponentu. Wystarczy, że będą ich używać. Nawet jeśli się zdarzy, że użytkownik komponentu oraz jego twórca to ta sama osoba (czyli Ty), to wciąż komponenty są łatwym sposobem implementacji możliwości funkcjonalnych.
Nie należy także zapominać o oczywistych korzyściach jakie daje stosowanie komponentów. Dzięki nim wzrasta możliwość wielokrotnego wykorzystywania tego samego kodu, dzięki czemu aplikacje są mniejsze. Kompilacja komponentów niezależnie od stron ASP.NET zwiększa efektywność działania tych stron. Łatwiejsza jest także pielęgnacja aplikacji — zmiana logiki biznesowej w jednym miejscu będzie od razu zauważalna w całej aplikacji. A co więcej, tworzone komponenty są elementami środowiska .NET, co oznacza, że w razie konieczności można je rozbudowywać lub używać przy tworzeniu innych komponentów.
Niemniej jednak, zdarza się, że wydzielenie poszczególnych warstw aplikacji nie jest sprawą oczywistą. W jakim miejscu należy przeprowadzić linię podziału pomiędzy interfejsem użytkownika, a logiką biznesową? To pytanie programiści zadają już od jakiegoś czasu. Przykłady podane w tym rozdziale mają za zadanie, w możliwie największym stopniu, pokazać jak należy rozdzielać obie warstwy. Czasami jednak, zależy to wyłącznie od oceny programisty.
W jaki sposób ASP.NET korzysta z komponentów
ASP.NET przechowuje skompilowane obiekty w folderze /bin, nazywanym także pamięcią podręczną komponentów. Gdy czytając ten rozdział stworzysz przykładowe komponenty, to po ich skompilowaniu, umieścisz je właśnie w tym folderze. Obiekty zapisane w tym folderze są automatycznie ładowane podczas uruchamiania aplikacji ASP.NET. Właśnie dzięki temu będziesz mógł używać własnych komponentów w tworzonych stronach ASP.NET.
Istnieje także możliwość ręcznego załadowania obiektów, które nie są przechowywane w folderze /bin. W tym celu wykorzystywany jest plik konfiguracyjny web.config, jednak zagadnienia te wykraczają poza ramy tematyczne niniejszej książki. W większości przypadków, wszystkie argumenty będą przemawiały za tym, aby własne obiekty przechowywać w folderze /bin.
Po załadowaniu własnych obiektów, można z nich korzystać tak samo, jak z wbudowanych obiektów ASP.NET. Na przykład, przestrzeń nazw System oraz wszystkie dostępne w niej klasy zostały skompilowane w formie jednego pliku, przechowywanego w globalnej pamięci podręcznej komponentów. Podczas tworzenia stron ASP.NET można zaimportować przestrzeń nazw System, bądź też odwoływać się do klas za pomocą pełnych nazw, na przykład: System.Integer. Jak się przekonasz w dalszej części rozdziału, dokładnie w taki sam sposób można korzystać z własnych obiektów.
Tworzenie obiektów biznesowych
Tworzenie obiektów biznesowych do złudzenia przypomina tworzenie kodu obsługi formularzy. Obiekty takie to po prostu klasy stworzone w języku VB.NET (lub innym języku którym potrafisz się posługiwać) i zorganizowane w logiczne grupy.
--> Ale nie marnujmy czasu i zabierzmy się w końcu za stworzenie przykładowego obiektu biznesowego. Ogólny szablon takiego obiektu przedstawiony został na listingu 15.1.[Author:p8R]
Listing 15.1. Podstawowa struktura obiektów biznesowych.
Imports System
Imports System.Data
Imports System.Data.OleDb
Namespace TYASPNET
Public Class Database
End Class
End Namespace
Analiza
Zapisz ten plik pod nazwą Database.vb. Ten obiekt biznesowy zapewni nam ogólne możliwości funkcjonalne związane z obsługą baz danych, z których będziemy korzystać przy tworzeniu stron ASP.NET. Za pośrednictwem tego obiektu będziemy mogli nawiązać połączenie z bazą danych, wykonać zapytanie oraz pobrać uzyskane wyniki. Innymi słowy obiekt reprezentuje bazę danych i powinien mieć wszystkie właściwości i metody które definiują taką bazę.
Postać powyższego przykładu przypomina format zapisu kodu obsługi formularzy. W pierwszej kolejności są importowane przestrzenie nazw, które będą wykorzystywane w tworzonym obiekcie. W tym przypadku są to przestrzenie System, System.Data oraz System.Data.OleDb (importowane odpowiednio w wierszach 1., 2. oraz 3.). W wierszu 5. definiowana jest przestrzeń nazw do której będzie należeć tworzony obiekt. Skąd pochodzi nazwa ASPNETDK? Znikąd — utworzyliśmy ją właśnie w tej chwili. Na tym przykładzie widać jak łatwo można rozszerzać środowisko .NET — sama deklaracja użycia nowej przestrzeni nazw powoduje jej automatyczne utworzenie. Tworząc nowe obiekty na potrzeby aplikacji można je dodawać do tej samej przestrzeni nazw. A zatem, przestrzeń nazw stanowi logiczną grupę, zawierającą wszystkie wykorzystywane obiekty biznesowe.
Notatka
Na przykład, przestrzeń nazw System.Data.OleDb zawiera wiele klas, takich jak OleDbCommand, OleDbConnection czy też OleDbDataAdapter. Jak widać, przestrzenie nazw są używane do logicznego grupowania klas.
Gdybyśmy chcieli, moglibyśmy także podać nazwę już istniejącej przestrzeni nazw, na przykład System.Web. W takim przypadku tworzony obiekt zostałby dodany do tej przestrzeni. Należy jednak pamiętać, iż przestrzenie nazw służą do logicznego grupowania obiektów. Nasze przykładowe obiekty nie pasują do żadnej z istniejących przestrzeni, a zatem powinniśmy je umieścić w nowej, stworzonej specjalnie dla nich. Można tworzyć osobne przestrzenie nazw dla poszczególnych tworzonych aplikacji ASP.NET.
Tak |
Nie |
Umieszczaj obiekty tworzone na potrzeby aplikacji ASP.NET w unikalnych przestrzeniach nazw. |
Nie umieszczaj własnych obiektów w predefiniowanych przestrzeniach nazw, może to bowiem wprowadzić zamieszanie i spowodować niepotrzebne problemy. |
I w końcu, w 7. wierszu listingu 15.1, została umieszczona deklaracja klasy. Na razie jest ona pusta, jednak już wkrótce dodamy do niej właściwości i metody. Nie należy także zapomnieć o dopisaniu instrukcji zamykających deklarację klasy oraz przestrzeni nazw.
Skompilujmy nasz przykładowy obiekt biznesowy, abyśmy mogli go użyć w stronie ASP.NET. W tym celu wykorzystamy kompilator języka VB.NET dostarczany wraz z .NET SDK. Aby użyć kompilatora kliknij przycisk Start i wybierz opcję Uruchom. Następnie, w wyświetlonym okienku dialogowym wpisz cmd.exe i kliknij przycisk OK — na ekranie pojawi się okno interpretera poleceń. Pierwszą czynnością jaką będziesz musiał teraz wykonać, jest przejście do głównego folderu aplikacji (w naszym przypadku jest to C:\inetpub\wwwroot\aspnetdlakazdego) i utworzenie folderu bin. W tym celu należy wydać polecenie:
mkdir bin
Za chwilę w tym folderze umieścimy nasz przykładowy obiekt biznesowy. Teraz przejdź do folderu w którym zapisałeś kod źródłowy tworzonego obiektu, na przykład: C:\inetpub\wwwroot\aspnetdlakazdego\rozdzial15. Następnie wpisz poniższe polecenie i naciśnij klawisz Enter:
vbc /t:library /out:..\bin\ASPNETDK.dll /r:System.dll /r:System.Data.dll
Database.vb
Kompilator języka VB.NET ma wiele opcji i jest bardzo skomplikowany; dlatego też omówimy tu wyłącznie opcje użyte w powyższym poleceniu. (Istnieje duże prawdopodobieństwo, że pisząc aplikacje ASP.NET nie będziesz musiał używać żadnych innych opcji.)
vbc.exe to nazwa kompilatora języka VB.NET. Program ten posłuży nam do skompilowania naszego przykładowego obiektu biznesowego do postaci biblioteki DLL, która będzie wykorzystywana w aplikacji ASP.NET. Parametr /t określa typ wynikowego pliku, który ma zostać utworzony. Library oznacza, że utworzony zostanie obiekt, który będzie mógł być wykorzystywany przez inne aplikacje, ale nie samodzielnie jak inne programy. Parametrowi /t można także przypisać wartość exe, co spowoduje wygenerowanie standardowego pliku wykonywalnego.
Parametr /out określa folder oraz nazwę pliku, w którym zostaną zapisane wyniki działania kompilatora. W naszym przypadku, wygenerowana biblioteka DLL ma zostać umieszczona w folderze /bin, a zatem, w parametrze /out podaliśmy względną ścieżkę dostępu do tego folderu oraz nazwę wynikowego pliku. Parametr /r oznacza odwołanie. W naszym przykładowym obiekcie biznesowym odwołujemy się do trzech przestrzeni nazw — System, System.Data oraz System.Data.OleDb. Przestrzeń nazw System została zapisana w pliku System.dll, natomiast pozostałe dwie przestrzenie — System.Data oraz System.Data.OleDb — w pliku System.Data.dll.
Ostrzeżenie
Pamiętaj, aby określać odwołania w poleceniu uruchamiającym kompilator a nie ograniczać się do instrukcji Import w kodzie źródłowym programów. Bez tych odwołań, polecenia Import nie będą miały żadnego znaczenia, a przy próbie kompilacji pliku pojawią się błędy.
Ostatnim elementem powyższego polecenia jest nazwa kompilowanego pliku — Database.vb. To właśnie ten plik zostanie skompilowany i umieszczony w pamięci podręcznej komponentów, gdzie będzie dostępny dla tworzonych stron ASP.NET. Wyniki kompilacji kodu z listingu 15.1 zostały przedstawione na rysunku 15.2.
Rysunek 15.2. Użycie kompilatora języka VB.NET do tworzenia obiektów biznesowych
Notatka
Nie przejmuj się, jeśli nie jesteś w stanie wprawnie posługiwać się kompilatorem VB.NET. Zazwyczaj będziesz musiał posługiwać się jednym i tym samym poleceniem, a jeśli jego składnia będzie musiała ulec zmianie — wyraźnie zaznaczę to w tekście. Więcej informacji na temat tego programu znajdziesz w dokumentacji .NET SDK.
Na listingu 15.2 przedstawiony został kod strony, która będzie korzystać z naszego przykładowego obiektu biznesowego.
Listing 15.2. Wykorzystanie obiektu biznesowego na stronie ASP.NET.
<%@ Page Language="VB" %>
<script runat="server">
sub Page_Load(obj as object, e as eventargs)
dim objDatabase as ASPNETDK.Database
lblMessage.Text = "Obiekt został pomyślnie utworzony!"
end sub
</script>
<html><body>
<asp:Label id="lblMessage" runat="server" />
</body></html>
Analiza
W wierszu 5. deklarowany jest nowy obiekt, bazujący na stworzonym przed chwilą obiekcie biznesowym. Jak na razie obiektu tego nie można w żaden sposób wykorzystać, gdyż nie implementuje on żadnych metod ani właściwości; zostaną one dodane w kolejnej części rozdziału, pod tytułem „Implementacja obiektów biznesowych”.
Warto zwrócić uwagę, iż przy tworzeniu obiektu została użyta jego pełna nazwa — ASPNETDK.Database. Można by także użyć samej nazwy klasy — Database — wymagało by to jedynie zaimportowania stworzonej przez nas przestrzeni nazw. W tym celu należało by użyć dyrektywy Import, podobnie jak przy importowaniu wszelkich innych przestrzeni nazw. Przykładowo załóżmy, że w 2. wierszu powyższego kodu została dodana następująca dyrektywa:
<%@ Import Namespace="ASPNETDK" %>
W takim przypadku instrukcję tworzącą nowy obiekt można by zapisać w postaci:
dim objDatabase as new Database
Dlaczego konieczna jest kompilacja obiektu?
Aż do momentu kompilacji, proces tworzenia naszego przykładowego obiektu biznesowego był niezwykle podobny do sposobu tworzenia kodu obsługi formularzy. A jednak kodu obsługi formularzy nie trzeba było kompilować. Dlaczego zatem konieczna jest kompilacja kodu źródłowego obiektów biznesowych?
Kod obsługi formularzy jest kasą ASP.NET zawierającą kod, który definiuje specyficzne możliwości funkcjonalne i z tego względu może być wykorzystywany wyłącznie na konkretnej stronie ASP.NET. Klasa ta nie będzie wykorzystywana przez żadną inną aplikację, a bardzo często nawet przez żadne inne strony ASP.NET. W momencie nadesłania żądania dotyczącego strony ASP.NET kompilowany jest zarówno kod strony jak i kod obsługi formularza. Następnie, metody i właściwości kodu obsługi formularza są wykorzystywane przez stronę ASP.NET. Jak zatem widać, w rzeczywistości kod obsługi formularzy też jest kompilowany.
Natomiast obiekty biznesowe z założenia są wykorzystywane w wielu różnych miejscach i nie należą do żadnej konkretnej strony ASP.NET. Obiekty te nie muszą także zawierać kodu służącego do interakcji z ASP.NET. Obiekty te będą wykorzystywane w wielu miejscach aplikacji, a może nawet w kilku różnych aplikacjach. Na przykład, nasz przykładowy obiekt Database będzie wykorzystywany w wielu różnych miejscach. Cały kod musi zostać skompilowany nim będzie go można użyć, a zatem będziemy musieli zawczasu skompilować nasz obiekt biznesowy, gdyż nie jest on skojarzony z żadną konkretną stroną ASP.NET.
Można także zawczasu kompilować kod obsługi formularza i wykorzystywać go w stronach ASP.NET jako obiekt biznesowy, ale dlaczego mielibyśmy utrudniać sobie życie? Kod obsługi formularza będzie wykorzystywany tylko w jednym miejscu i tak czy inaczej zostanie skompilowany. A zatem nie ma żadnego powodu, aby wcześniej kompilować go ręcznie.
Implementacja obiektów biznesowych
Wróćmy do naszego przykładowego obiektu biznesowego z listingu 15.1. i dodajmy do niego jakieś możliwości funkcjonalne. Ponieważ obiekt ten ma reprezentować bazę danych, pierwszą rzeczą jaka będzie w nim potrzebna jest właściwość określająca łańcuch połączenia. Właściwość ta zostanie zaimplementowana w klasie Database, jak pokazano na listingu 15.3.
Listing 15.3. Właściwość określająca łańcuch połączenia.
Imports System
Imports System.Data
Imports System.Data.OleDb
Namespace ASPNETDK
Public Class Database
public ConnectionString as String
private objConn as OleDbConnection
private objCmd as OleDbCommand
Każda strona wykorzystująca ten obiekt biznesowy będzie w stanie, w dowolnej chwili, określić oraz pobrać łańcuch połączenia. Do kodu zostały także dodane dwie prywatne zmienne reprezentujące obiekty bazy danych. Ponieważ są to zmienne prywatne, będą one dostępne dla metod klasy Database, jednak nie poza nimi. Innymi słowy, strona ASP.NET wykorzystująca ten obiekt biznesowy nie będzie w stanie uzyskać do nich dostępu; będzie natomiast miała dostęp do publicznej zmiennej ConnectionString.
Podpowiedź
Możesz wyobrazić sobie klasę jako silnik samochodu. Wszystkie prywatne elementy klasy są ukryte wewnątrz „silnika” i nie można ich zobaczyć z zewnątrz. Zapewne wiesz, że wewnątrz silnika znajdują się cylindry i tłoki, lecz nie możesz ich zobaczyć ani dotknąć (chyba że rozbierzesz silnik na części, ale to już zupełnie inna sprawa). Z drugiej strony, publiczne elementy klasy są dostępne dla „świata zewnętrznego”. W prawdziwym silniku takimi elementami są na przykład: prętowy wskaźnik poziomu oleju, gniazda do wkręcania świec czy też filtr powietrza.
Kolejnym elementem, który będzie nam potrzebny, są metody pobierające informacje z bazy danych. Nasz przykładowy obiekt biznesowy zostanie wyposażony w metodę, która wykona podane zapytania SQL i zwróci uzyskane wyniki w formie obiektu OleDbDataReader. Przyda się nam także metoda, która pozwoli na wykonywanie operacji na bazie danych, lecz nie będzie zwracać żadnych wyników. Dzięki niej, będzie można wykonywać, na przykład, polecenia SQL Insert oraz Update. Na listingu 15.4. przedstawione zostały dwie metody służące do wykonywania zapytań SQL, pierwsza z nich zwraca dane, a druga nie.
Listing 15.4. Metody klasy Database służące do wykonywania poleceń SQL.
public function SelectSQL(strSelect as string) as _
OleDbDataReader
try
objConn = new OleDbConnection(ConnectionString)
objCmd = new OleDbCommand(strSelect, objConn)
objCmd.Connection.Open
return objCmd.ExecuteReader
objCmd.Connection.Close()
catch ex as OleDbException
return nothing
end try
end function
public function ExecuteNonQuery(strQuery as string) as _
Boolean
try
objConn = new OleDbConnection(ConnectionString)
objCmd = new OleDbCommand(strQuery, objConn)
objCmd.Connection.Open()
objCmd.ExecuteNonQuery
objCmd.Connection.Close()
return true
catch ex as OleDbException
return false
end try
end function
End Class
End Namespace
Metoda SelectSQL jest zupełnie standardowa. Otwiera ona połączenie przy wykorzystaniu obiektu OleDbCommand, wykonuje zapytanie, po czym zwraca wyniki w formie obiektu OleDbDataReader (patrz wiersz 18.). W wierszu 19. zamykane jest połączenie z bazą danych. Jeśli w trakcie wykonywania metody cokolwiek pójdzie nie tak jak należy, to blok try przechwyci zgłaszane błędy i zwróci wartość nothing.
Metoda ExecuteNonQuery jest bardzo podobna, lecz nie zwraca obiektu OleDbDataReader. Zamiast tego zwracana jest wartość logiczna true bądź false określająca czy polecenie SQL zostało wykonane poprawnie czy nie.
Ponownie skompiluj kod źródłowy naszego obiektu biznesowego posługując się tym samym poleceniem co poprzednio:
vbc /t:library /out:..\bin\ASPNETDK.dll /r:System.dll /r:System.Data.dll
Database.vb
A teraz zmodyfikujmy przykładową stronę ASP.NET wykorzystującą ten obiekt biznesowy. Na listingu 15.5. przedstawiony został zmodyfikowany kod strony. Jak widać posługuje się ona właściwością ConnectionString oraz metodą SelectSQL obiektu Database i wyświetla wyniki przy użyciu elementu sterującego DataGrid.
Listing 15.5. Wykorzystanie metod i właściwości obiektu biznesowego.
<%@ Page Language="VB" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<script runat="server">
sub Page_Load(obj as object, e as eventargs)
dim objDatabase as new ASPNETDK.Database
objDatabase.ConnectionString = "Provider=" & _
"Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\ASPNET\data\banking.mdb"
dim objReader as OleDbDataReader
objReader = objDatabase.SelectSQL _
("Select * from tblUsers")
if not objReader is nothing then
DataGrid1.DataSource = objReader
DataGrid1.DataBind()
objReader.Close
end if
end sub
</script>
<html><body>
<asp:Label id="lblMessage" runat="server" />
<asp:DataGrid id="DataGrid1"
runat="server" BorderColor="black"
GridLines="Vertical" cellpadding="4"
cellspacing="0" width="100%"
Font-Name="Arial" Font-Size="8pt"
HeaderStyle-BackColor="#cccc99"
ItemStyle-BackColor="#ffffff"
AlternatingItemStyle-Backcolor="#cccccc" />
</body></html>
Analiza
W wierszu 7. powyższego przykładu deklarowany jest obiekt Database, a w wierszu 9. określana wartość właściwości ConnectionString. Następnie, w wierszu 14. wywoływana jest metoda SelectSQL przedstawiona na listingu 15.4. Metoda ta pobiera wyniki z bazy danych i zwraca je w formie obiektu OleDbDataReader.
Pamiętasz zapewne, że w przypadku wystąpienia jakichkolwiek błędów, metoda SelectSQL zwraca wartość nothing. Dlatego, zanim w jakikolwiek sposób wykorzystamy obiekt czytelnika (OleDbDataReader), musimy sprawdzić, czy w ogóle dysponujemy jakimikolwiek danymi. Jeśli informacje zostały poprawnie pobrane z bazy danych, są one wiązane z elementem sterującym DataGrid (patrz wiersz 19.), po czym obiekt czytelnika jest zamykany (w wierszu 20.). Wyniki wykonania powyższej strony zostały przedstawione na rysunku 15.3.
Rysunek 15.3. Przykładowy obiekt biznesowy zwraca dane, które można wykorzystać na stronie ASP.NET
--> Notatka !!!!!!!!!!!!!!!!!!!!!!!!!!!!![Author:p8R]
Wykorzystania stworzonego przez nas obiektu Database niczym się nie różni od sposobów użycia wszystkich innych obiektów stosowanych do tej pory przy tworzeniu stron ASP.NET. W standardowy sposób tworzona jest nowa kopia obiektu, określane wartości jego właściwości i wywoływane metody.
Być może zastanawiasz się dlaczego wykonaliśmy tą całą pracę tylko i wyłącznie po to, by zaoszczędzić kilka wierszy kodu w stronie ASP.NET. W przypadku prostych stron, takich jak nasza, tworzenie obiektu biznesowego w celu wykonania zapytania SQL i pobrania wyników z bazy danych nie jest konieczne. Jednak gdy Twoje tworzone aplikacje będą znacznie większe, obiekty biznesowe mogą się stać znacznie bardziej skomplikowane. W tym przypadku wykorzystanie tych obiektów przy tworzeniu stron ASP.NET może zaoszczędzić bardzo wiele czasu.
Poza tym nasz przykładowy obiekt biznesowy może zostać wykorzystany na dowolnej stronie ASP.NET. Można w nim zmienić łańcuch zapytania, aby nawiązać połączenie z inną bazą danych i podać inne zapytanie SQL, aby pobrać zupełnie nowe wyniki. Choć w przedstawionym przykładzie wykorzystanie obiektu biznesowego przyniosło korzyści w postaci skrócenia kodu strony ASP.NET o do 10 do 15 wierszy, wyobraź sobie ile można by zyskać gdyby w tworzonej aplikacji było 10 lub 20 takich stron.
Możesz się także zastanawiać dlaczego stworzyliśmy obiekt biznesowy reprezentujący bazę danych, jeśli mogliśmy wykorzystać klasy OleDb. Czy nie można było stworzyć czegoś bardziej użytecznego? Otóż w rzeczywistości nasz obiekt biznesowy jest bardzo przydatny. Obiekt ten nie tylko pozwala użytkownikom na pobieranie informacji z baz danych przy użyciu jednego wiersza kodu, lecz także jednocześnie ukrywa całą złożoność procesu wykorzystania bazy danych. Użytkownik tego obiektu (w tym przypadku inny programista) nie musi się przejmować tworzeniem obiektu OleDbCommand ani nawiązywaniem połączenia z bazą. Nie musi zawracać sobie głowy umieszczaniem instrukcji w bloku try, ani przechwytywaniem i obsługą ewentualnych błędów. Nasza klasa wykonuje wszystkie te czynności za użytkowników obiektu biznesowego. Na rysunku 15.4. została przedstawiona różnica pomiędzy tym co widzi użytkownik obiektu, a faktycznie wykonywanymi czynnościami.
Rysunek 15.4. Programista widzi wyłącznie to na co pozwoli mu twórca obiektu biznesowego; cała złożoność implementacji została przed nim ukryta
Opis rysunku
What the user doesn't see — To czego użytkownik nie widzi
What the user sees — To co użytkownik widzi
Object — Obiekt
Properties — Właściwości
Methods — Metody
ConnectionString, SelectSQL, ExecuteNonQuery — bez zmian
try
objConn = new OleDbConnection(ConnectionString)
objCmd = new OleDbCommand(strSelect, objConn)
objCmd.Connection.Open
return objCmd.ExecuteReader
objCmd.Connection.Close()
catch ex as OleDbException
return nothing
Praktyczny przykład
Teraz, kiedy już wiesz jak napisać prosty obiekt biznesowy, wykorzystajmy tę wiedzę do stworzenia czegoś bardzie przydatnego. Wiele witryn posiada obszary dostępne wyłącznie dla zarejestrowanych użytkowników lub strony, których zawartość zarejestrowani użytkownicy mogą dostosowywać do własnych potrzeb. W takich witrynach warto wydzielić kod związany z obsługą zarejestrowanych użytkowników. Wszystkie możliwości funkcjonalne związane z obsługą użytkowników, takie jak logowanie, uaktualnianie informacji o użytkowniku przechowywanych w bazie danych i tak dalej, można z łatwością zaimplementować w postaci obiektu biznesowego.
W zasadzie obiekt ten będzie reprezentować użytkownika ogólnego i powinien zawierać wszystkie właściwości i metody konieczne do pełnego opisania tego użytkownika. Powinien on także zawierać metody służące do wykonywania operacji związanych z obsługą użytkowników, takich jak dodawanie nowych, usuwanie niepotrzebnych użytkowników oraz uwierzytelnianie użytkowników. Konkretnie rzecz biorąc chcemy, aby obiekt ten zawierał:
obiekt reprezentujący tożsamość użytkownika, w tym jego imię i nazwisko, nazwę użytkownika, identyfikator, i tak dalej;
metody służące do dodawania nowych i usuwania starych użytkowników oraz aktualizacji danych użytkowników;
metody umożliwiające uwierzytelnianie użytkowników.
Powyższe możliwości funkcjonalne zostaną zaimplementowane jako dwie niezależne klasy. Pierwsza z nich będzie reprezentować szczegółowe informacje o użytkowniku, a druga udostępni metody pozwalające na wykonywanie opisanych wcześniej czynności związanych z obsługą użytkowników. W ten sposób wszystkie dane użytkownika będą mogły być traktowane jako odrębna, niezależna całość, na której można wykonywać jakieś operacje. Wzajemne zależności pomiędzy tymi dwoma obiektami zostały przedstawione na rysunku 15.5.
Rysunek 15.5. Informacje na temat użytkownika będą przechowywane w obiektach klasy UserDetail, natomiast wszelkie możliwości funkcjonalne związane z obsługą użytkowników, zostaną zaimplementowane w klasie User
Opis rysunku
ASP.NET page — Strona ASP.NET
UserDetails, User — bez zmian
Firstname, Lastname — Firstname, LastName, i tak dalej
Update data — Aktualizacja danych
Retrieve data — Pobranie danych
Login user … — Zalogowanie użytkownika, Pobranie informacji, i tak dalej
Database — Baza danych
W przykładzie tym możesz wykorzystać bazę danych użytkowników stworzoną w rozdziale 8, pt.: „Podstawowe wiadomości na temat tworzenia baz danych”. Początek kodu naszego nowego, przykładowego obiektu biznesowego został przedstawiony na listingu 15.6.
Listing 15.6. Klasa UserDetails (User.vb).
Imports System
Imports System.Data
Imports System.Data.OleDb
Namespace TYASPNET
Public Class UserDetails
public FirstName as string
public LastName as string
public UserName as string
public Password as string
public UserID as string
End Class
Analiza
Zapisz powyższy fragment kodu w pliku User.vb. Jak widać rozpoczyna się on tak samo jak obiekt biznesowy stworzony we wcześniejszej części rozdziału. W wierszach od 1. do 3. są importowane używane przestrzenie nazw, a w wierszu 5. jest deklarowana przestrzeń nazw do której należy tworzony obiekt. Deklaracja klasy UserDetails zaczyna się w wierszu 7. i zawiera wyłącznie właściwości, które będą potrzebne do reprezentacji użytkowników. (Odpowiadają one informacjom o użytkownikach przechowywanym w bazach danych; niektóre z informacji dostępnych w bazie zostały jednak pominięte w celu uproszczenia naszego przykładu. Jeśli chcesz będziesz je mógł dodać.)
Kolejną częścią naszego obiektu biznesowego będzie klasa User, implementująca potrzebne nam możliwości funkcjonalne. Jej kod został przedstawiony na listingu 15.7.
Listing 15.7. Klasa User (User.vb).
Public Class User
public function Login(UserName as string, Password as _
string) as string
dim intId as string = "0"
dim Conn as new OleDbConnection("Provider=" & _
"Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\ASPNET\data\banking.mdb")
dim objCmd as OleDbCommand = new OleDbCommand _
("SELECT UserID FROM tblUsers WHERE " & _
"UserName = '" & UserName & "' AND " & _
"Password = '" & Password & "'", Conn)
dim objReader as OleDbDataReader
try
objCmd.Connection.Open()
objReader = objCmd.ExecuteReader
do while objReader.Read
intId = objReader.GetInt32(0).ToString
loop
catch ex as OleDbException
throw ex
end try
return intID
end function
public function GetDetails(UserID as integer) as _
UserDetails
dim Conn as new OleDbConnection("Provider=" & _
"Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\ASPNET\data\banking.mdb")
dim objCmd as OleDbCommand = new OleDbCommand _
("SELECT FirstName, LastName, UserName, " & _
"Password FROM tblUsers WHERE UserID = " & _
UserID, Conn)
dim objReader as OleDbDataReader
try
objCmd.Connection.Open()
objReader = objCmd.ExecuteReader
catch ex as OleDbException
throw ex
end try
dim objDetails as new UserDetails
while objReader.Read()
objDetails.FirstName = objReader.GetString(0)
objDetails.LastName = objReader.GetString(1)
objDetails.UserName = objReader.GetString(2)
objDetails.Password = objReader.GetString(3)
objDetails.UserID = UserID.ToString
end while
objReader.Close
return objDetails
end function
public function Update(objDetails as UserDetails, _
intUserID as integer) as boolean
dim objOldDetails as new UserDetails
objOldDetails = GetDetails(intUserID)
with objDetails
if .FirstName = "" then
.FirstName = objOldDetails.FirstName
end if
if .LastName = "" then
.LastName = objOldDetails.LastName
end if
if .Username = "" then
.UserName = objOldDetails.UserName
end if
if .Password = "" then
.Password = objOldDetails.Password
end if
end with
dim Conn as new OleDbConnection("Provider=" & _
"Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=C:\ASPNET\data\banking.mdb")
dim strSQL as string = "UPDATE tblUsers SET " & _
"FirstName = '" & objDetails.FirstName & "', " & _
"LastName = '" & objDetails.LastName & "', " & _
"UserName = '" & objDetails.UserName & "', " & _
"[Password] = '" & objDetails.Password & "' " & _
"WHERE UserID = " & intUserID
dim objCmd as OleDbCommand = new OleDbCommand _
(strSQL, Conn)
try
objCmd.Connection.Open()
objCmd.ExecuteNonQuery
catch ex as OleDbException
throw ex
finally
objCmd.Connection.Close
end try
return true
end function
End Class
End Namespace
Analiza
Jak widać klasa User jest całkiem spora, a powyższy kod nie zawiera jeszcze wszystkich metod jakie mają się w niej znaleźć. Na szczęście zrozumienie zasad działania metod tej klasy nie powinno przysporzyć Ci problemów i dlatego przeanalizujemy je dosyć pobieżnie.
Metoda Login rozpoczynająca się w wierszu 15. pobiera nazwę użytkownika i hasło i porównuje je z informacjami zapisanymi w bazie danych. Jeśli przekazane informacje reprezentują istniejącego użytkownika, metoda ta zwraca wartość odpowiedniego pola UserID (czyli identyfikator użytkownika).
Metoda GetDetails rozpoczynająca się w wierszu 42. pobiera identyfikator użytkownika (UserID) i pobiera z bazy danych informacje na jego temat. W wierszu 61. tworzony jest nowy obiekt klasy UserDetails. Metoda przypisuje właściwościom tego obiektu przypisywane informacje pobrane z bazy danych, a następnie zwraca go jako wynik swego działania. Zapisując informacje o użytkowniku w publicznych właściwościach obiektu UserDetails, zapewniana jest możliwość łatwego dostępu do nich.
Metoda Update aktualizuje wiersz bazy danych zawierający informacje o konkretnym użytkowniku. W wywołaniu tej metody przekazywany jest obiekt UserDetails zawierający zaktualizowane dane dla każdej z właściwości opisujących użytkownika. Co się jednak dzieje jeśli trzeba zmienić tylko jedną wartość? Nie chcemy, aby użytkownik musiał podawać wszystkie wartości, jeśli zmianie mają ulec tylko niektóre z nich. Właśnie z tego powodu, w obiekcie przekazywanym w wywołaniu tej metody, można podać tylko te właściwości, które mają zostać zmienione; metoda sam uzupełni pozostałe właściwości aktualnymi informacjami o użytkowniku. Te możliwości funkcjonalne zostały zaimplementowane w wierszach od 77. do 93.
Zmienna objOldDetails deklarowana w wierszu 77. przechowuje oryginalne informacje o użytkowniku, które są pobierane z bazy danych przy użyciu metody GetDetails. Zapisana poniżej grupa instrukcji if sprawdza które z właściwości opisujących użytkownika nie zostały podane i przypisuje im oryginalne wartości. Po uzupełnieniu informacji, na podstawie obiektu objDetails można już utworzyć zapytanie SQL bez obawy utracenia jakichkolwiek informacji.
Podpowiedź
Instrukcja With obiekt zapisana w wierszu 80. informuje, że wszystkie umieszczone wewnątrz niej odwołania do zmiennych poprzedzonych kropką, odnoszą się do wskazanego obiektu. Na przykład instrukcje
objDetails.FirstName = ""
objDetails.LastName = ""
oznaczają dokładnie to samo co poniższy fragment kodu:
With objDetail
.FirstName = ""
.LastName = ""
end with
Wykorzystanie instrukcji With może zaoszczędzić nieco pisania jeśli należy podać wartości wielu właściwości obiektu.
Tworzenie polecenia UPDATE SQL rozpoczyna się w wierszu 99., a samo polecenie wykonywane jest wewnątrz bloku try. Jeśli aktualizacja danych przebiegnie pomyślne, to metoda zwraca wartość true.
Kod przedstawiony na powyższym listingu dopisz do pliku User.vb. Gdy to zrobisz przejdź do odpowiedniego folderu i skompiluj obiekt posługując się następującym poleceniem:
vbc /t:library /out:..\bin\User.dll /r:System.dll /r:System.Data.dll
User.vb
Podpowiedź
Jeśli chcesz, to nic nie stoi na przeszkodzie, aby wyniki kompilacji wielu różnych plików umieścić w jednej bibliotece DLL. Na przykład:
vbc /t:library /out:..\bin\ASPNETDK.dll /r:System.dll /r:System.Data.dll User.vb Database.vb
Powyższe polecenie skompiluje zarówno plik z obiektem biznesowym służącym do obsługi bazy danych jak również obiekt biznesowy do obsługi użytkowników, a wyniki kompilacji zapisze w jednym pliku. Zarządzanie jedną biblioteką DLL zawsze będzie łatwiejsze niż kilkoma. W rzeczywistości, zamiast podawać nazwy wszystkich plików jakie mają wejść w skład tworzonej biblioteki DLL, można posłużyć się znakami wieloznacznymi. Na przykład, poniższe polecenie łączy wszystkie pliki z rozszerzeniem .vb znajdujące się w bieżącym folderze i kompiluje je do jednej biblioteki DLL:
vbc /t:library /out:..\bin\ASPNETDK.dll /r:System.dll
/r:System.Data.dll *.vb
Wydając takie polecenie powinieneś jednak mieć pewność, że chcesz, aby wszystkie pliki .vb znajdujące się w bieżącym folderze zostały skompilowane razem.
Listing 15.8. przedstawia stronę ASP.NET wykorzystującą nasze nowe obiekty biznesowe (zarówno User jak i UserDetails).
Listing 15.8. Wykorzystanie obiektów biznesowych.
<%@ Page Language="VB" %>
<script runat="server">
sub Page_Load(obj as object, e as eventargs)
if Not Page.IsPostBack then
dim objUser as new ASPNETDK.User
dim objDetails as new ASPNETDK.UserDetails
objDetails = objUser.GetDetails(1)
lblMessage.Text = "Witaj " & _
objDetails.FirstName & "!"
end if
end sub
sub Update(obj as object, e as eventargs)
dim objUser as new ASPNETDK.User
dim objDetails as new ASPNETDK.UserDetails
objDetails.FirstName = tbName.Text
if objUser.Update(objDetails, 1) then
objDetails = objUser.GetDetails(1)
lblMessage.Text = "Witaj " & _
objDetails.FirstName & "!"
else
lblMessage.Text = "Aktualizacja danych nie powiodła się!"
end if
end sub
</script>
<html><body>
<form runat="server">
<asp:Label id="lblMessage" runat="server" /><p>
Zmieniasz imię?<br>
<asp:Textbox id="tbName" runat="server"/><br>
<asp:Button id="btSubmit" runat="server"
OnClick="Update" Text="Wyślij" />
</form>
</body></html>
Gdy użytkownik po raz pierwszy wyświetli tę stronę, zobaczy na niej wiadomość powitalną wygenerowaną dla użytkownika o identyfikatorze 1. W procedurze obsługi zdarzenia Page_Load tworzone są dwa nowe obiekty, a metoda GetDetails pobiera z bazy informacje i zwraca obiekt UserDetails. Następnie wyświetlana jest prosta wiadomość powitalna.
Gdy użytkownik wpisze nowe imię w polu tekstowym i kliknie przycisk Wyślij, wykonywana jest procedura Update. Metoda ta tworzy nowy obiekt User oraz UserDetails, przypisuje podane przez użytkownika imię właściwości FirstName obiektu UserDetails, po czym wywołuje metodę User.Update. Jeśli aktualizacja danych zakończy się pomyślnie, to metoda User.Update zwróci wartość true i zostanie wygenerowana wiadomość powitalna dla nowego imienia. Wyniki wykonania powyższej strony po zmianie imienia, zostały przedstawione na rysunku 15.6.
Rysunek 15.6. Obiekty biznesowe manipulują danymi użytkownika choć konieczny kod strony ASP.NET został skrócony do zaledwie kilku wierszy
Teraz nasza strona ASP.NET jest bardzo krótka. Strona o takich samych możliwościach funkcjonalnych, w której nie byłyby wykorzystywane obiekty biznesowe musiałaby być znacznie dłuższa. Nasza strona zawiera teraz wyłącznie kod bezpośrednio związany z obsługą interfejsu użytkownika. Także wszystkie inne strony, na których trzeba będzie zaimplementować te same możliwości funkcjonalne, nie będą już musiały bezpośrednio używać obiektów związanych z obsługą baz danych.
Wyobraź sobie, co by się działo jeśli musielibyśmy dodać nową metodę do obiektu User lub zmienić strukturę bazy danych. Jeśli wszystkie te funkcje zostałyby zaimplementowane na wszystkich stronach ASP.NET które musiałyby z nich korzystać, to jakakolwiek zmiana struktury bazy danych pociągnęłaby za sobą konieczność zmiany wszystkich tych stron ASP.NET. Dzięki wykorzystaniu obiektu biznesowego wszystkie konieczne zmiany należy wprowadzić w jednym pliku .vb i ponownie go skompilować. Cały problem zostanie rozwiązany bez konieczności modyfikowania jakichkolwiek stron ASP.NET.
Podpowiedź
Zamiast od podstaw implementować możliwości funkcjonalne związane z obsługą bazy danych w obiekcie User, można by także wykorzystać obiekt biznesowy Database stworzony we wcześniejszej części rozdziału. W takim przypadku, podczas kompilacji obiektu User należałoby się posłużyć następującym poleceniem:
vbc /t:library /out:..\bin\User.dll /r:System.dll
/r:System.Data.dll /r:ASPNETDK.dll User.vb
Konkretnie rzecz biorąc do wcześniej używanego polecenia należy dodać odwołanie do biblioteki ASPNETDK.dll.
Kilka spraw jakie należy wziąć pod uwagę
Zauważ, iż w pliku User.vb na stałe podaliśmy kilka informacji, takich jak łańcuch połączenia z bazą danych. Informacje tego typu nie należą w zasadzie do obiektu biznesowego i należało by umieścić je w osobnym pliku konfiguracyjnym, na przykład — web.config. Obiekt biznesowy z łatwością mógłby odczytać taki łańcuch znaków przy wykorzystaniu metody GetConfig obiektu klasy HttpContext (więcej informacji na ten temat znajdziesz w rozdziale 18., pt.: „Konfiguracja i wdrażanie aplikacji ASP.NET”). Dzięki temu tworzone aplikacje mogą być znacznie bardziej elastyczne, gdyż nie będą one wymagały ponownej rekompilacji w przypadku zmiany bazy danych.
Sam fakt że obiekt biznesowy nie powinien być zależny od używanej bazy danych nie oznacza wcale, że nie ma mieć także żadnej „świadomości” jaka baza jest używana. Wykorzystanie w obiekcie biznesowym poleceń SQL charakterystycznych dla danej bazy danych, nie jest niczym złym, a w praktyce jest to bardzo często spotykane rozwiązanie. Innymi słowy obiekt biznesowy może zależeć od formatu wykorzystywanej bazy danych, lecz nie od jej położenia.
Oczywiście, wszystkie wykorzystywane polecenia SQL można także zaimplementować w formie procedur zachowanych. Dzięki temu nasz obiekt biznesowy mógłby przekazywać do nich parametry. W takim przypadku, jeśli tylko nazwy procedur pozostaną takie same, to będzie można dodawać do nich nowe możliwości funkcjonalne bez konieczności modyfikacji kodu źródłowego obiektu biznesowego i jego ponownej kompilacji.
Tworząc obiekty biznesowe zawsze należy myśleć o logicznym rozdzieleniu możliwości funkcjonalnych. Dwa ostatnie obiekty reprezentowały użytkownika. Nie powinny one zawierać żadnego kodu wyświetlającego informacje na stronie, ani poleceń SQL zwracających dane. Obiekty te powinny zawierać wyłącznie metody i właściwości związane z obsługą użytkowników. Taki sposób podejścia może pomóc przy projektowaniu aplikacji.
Nie należy również próbować implementować zbyt wielu możliwości funkcjonalnych w jednym obiekcie biznesowym. Na przykład, na ostatnim przykładzie mogłeś się przekonać, że informacje o użytkowniku można w prosty i logiczny sposób oddzielić od metod związanych z obsługą użytkownika. A zatem, zamiast jednego obiektu dysponującego wszystkimi możliwościami funkcjonalnymi, można stworzyć dwa obiekty wzajemnie od siebie zależne.
Wykorzystanie komponentów stworzonych poza środowiskiem .NET
Przypomnij sobie, że kompilując obiekty w środowisku .NET, muszą one wygenerować opisujące je „metadane”. Maszyna wirtualna CLR (Common Language Runtime) wykorzystuje te dane do załadowania obiektu bez jakiejkolwiek pomocy ze strony programisty. Wystarczy umieścić obiekt w folderze /bin i bez żadnych problemów można z niego korzystać.
Jednak wcześniejsze obiekty, które nie były tworzone w środowisku .NET (zazwyczaj określane jako obiekty Component Object Model, lub w skrócie obiekty COM) nie dysponują możliwością generacji metadanych. Wcześniej nie było żadnej wirtualnej maszyny CLR, która mogłaby zarządzać takimi obiektami i ładować je do pamięci, ani żadnych metadanych, które poinformowałyby wirtualną maszynę o fakcie istnienia obiektów. Programiści musieli ręcznie rejestrować komponenty przy wykorzystaniu programu REGSVR32.exe, który zapisywał informacje o komponentach w Rejestrze systemu Windows (miejsca w którym gromadzone były wszelkie informacje na temat aplikacji i komponentów zainstalowanych na danym komputerze).
Notatka
Obiekty COM są „starsze” gdyż technologia wykorzystywana do ich tworzenia jest starsza niż technologia .NET — a nie dlatego że same obiekty są starsze.
Obiekty COM nie były tworzone w środowisku .NET, a zatem CLR nie zarządza nimi, dlatego też są one czasami określane jako „kod niezarządzany” (ang.: unmanaged code). Z podanych powodów ASP.NET ma nieco większe problemy z określeniem w jaki sposób należy korzystać z tych obiektów. Jest to jednym z powodów dla których nie można określać właściwości obiektów COM podczas projektowania programów.
ASP.NET wciąż pozwala na korzystanie z tych obiektów — są one tworzone przy użyciu metody CreateObject klasy HttpServerUtility. Metoda ta pobiera argument będący łańcuchem znaków opisującym położenie obiektu w Rejestrze i wykorzystuje go do określenia właściwości obiektu. Ten szczególny łańcuch znaków określany jest jako progId. Poniżej przedstawiona została składnia wywołania tej metody:
Server.CreateObject(progId)
Na przykład, w klasycznych stronach ASP operacje wejścia/wyjścia były realizowane przy wykorzystaniu obiektu FileSystemObject należącego do biblioteki Scripting. Obiekt ten udostępnia niemal te same możliwości funkcjonalne co klasy omówione w rozdziale 13, pt.: „Odczytywanie i zapisywanie plików na serwerze WWW”. Aby użyć tego obiektu na tworzonych stronach ASP.NET należałoby posłużyć się następującym fragmentem kodu:
dim objFSO as object
objFSO = Server.CreateObject("Scripting.FileSystemObject")
Przykład przedstawiony na listingu 15.9 demonstruje w jaki sposób można wyświetlić ścieżkę dostępu do pliku przy wykorzystaniu obiektu FileSystemObject.
Listing 15.9. Wykorzystanie obiektów COM.
<%@ Page Language="VB" %>
<script runat="server">
sub Page_Load(obj as object, e as eventargs)
dim objFSO, objFile
objFSO = Server.CreateObject _
("Scripting.FileSystemObject")
objFile = objFSO.GetFile _
(Server.MapPath("../rozdzial13/log.txt"))
lblMessage.Text = objFile.Path
end sub
</script>
<html><body>
<asp:Label id="lblMessage" runat="server" />
</body></html>
Analiza
W powyższym przykładzie, w wierszach 6. i 8. tworzone są, odpowiednio, obiekty Scripting.FileSystemObject oraz Scripting.File. Po utworzeniu, obiektów tych można używać do wykonywania pewnych operacji wejścia/wyjścia, takich jak wyświetlanie pełnej ścieżki dostępu do pliku. Wyniki wykonania powyższej strony ASP.NET zostały przedstawione na rysunku 15.7.
Rysunek 15.7. ASP.NET umożliwia wykorzystanie starszych obiektów COM
Jednak wykorzystanie obiektu FileSystemObject nie jest szczególnie interesujące, gdyż środowisko .NET udostępnia znacznie lepsze klasy, takie jak TextReader oraz TextWriter, które są „w pełni obiektowe”. W zasadzie, realne korzyści z możliwości użycia obiektów COM w środowisku .NET odczują wyłącznie programiści, którzy dysponują już wieloma własnymi obiektami COM. Firmy posiadające własne witryny WWW bardzo często wykorzystywały kilka obiektów COM w celu wykonywania tych samych czynności, do których my wykorzystaliśmy w tym rozdziale obiektów biznesowych. Postawienie tych wszystkich firm wobec konieczności przepisania tych wszystkich obiektów COM i zamienienia je na obiekty biznesowe środowiska .NET, byłoby koszmarną stratą czasu.
Także wiele aplikacji udostępnia swoje możliwości funkcjonalne pod postacią obiektów COM. Na przykład, dzięki metodom udostępnianym przez program Microsoft Word można tworzyć i operować na jego dokumentach z poziomu stron ASP.NET. Wszystkie obiekty COM udostępniane przez ten program są przykładami kodu niezarządzanego. Niemniej jednak w żadnym stopniu nie ogranicza to naszych możliwości wykorzystania tych obiektów w środowisku .NET.
Nowe określenie
Wykorzystanie obiektów COM na jednak pewną wadę. Obiekty te nie dysponują żadnymi metadanymi które by je opisywały, dlatego też ASP.NET ma więcej problemów z określeniem typów danych jakie obiekty te pobierają i zwracają, i dlatego bardzo często koniecznej jest stosowanie konwersji typów. Proces ten określany jest mianem szeregowania (ang.: marshaling). Szeregowanie, ze względu na konieczność wykonywania dodatkowych operacji, pogarsza efektywność działania aplikacji; z tego względu należy unikać stosowania obiektów COM.
Na szczęście środowisko .NET zostało wyposażone w program narzędziowy, który służy do konwersji obiektów COM do obiektów .NET. Program ten, nazywany importerem biblioteki typów (ang.: Type Library Importer), analizuje obiekty COM i tworzy odpowiednie metadane pozwalając na łatwiejsze wykorzystanie tych obiektów w aplikacjach ASP.NET. Pogram ten nosi nazwę tlbimp.exe.
Przykładowo załóżmy, że w naszych stronach ASP.NET chcielibyśmy wykorzystać obiekt FileSytemObject, lecz nie mamy ochoty niepotrzebnie zmniejszać efektywności działania aplikacji, ze względu na konieczność wykonywania procesu szeregowania. Obiekt FileSytemObject jest umieszczony w bibliotece scrrun.dll, która zazwyczaj jest przechowywana w folderze c:\winnet\sytsem32. Otwórz okno wiersza poleceń i przejdź do tego folderu. Następnie wpisz poniższe polecenie:
tlbimp scrrun.dll /out:scrrun_net.dll
Polecenie to utworzy nową bibliotekę o nazwie scrrun_net.dll bazującą na bibliotece COM o nazwie scrrun.dll. Komunikaty wyświetlane w oknie wiersza poleceń, powinny przypominać te, przedstawione na rysunku 15.8.
Rysunek 15.8. Program tlbimp.exe importuje obiekty COM do środowiska .NET
Skopiuj ten plik do pamięci podręcznej komponentów .NET (c:\inetpub\wwwroot\aspnetdlakazdego\bin). Teraz możemy zmodyfikować kod z listingu 15.9 i wykorzystać w nim nową wersję obiektu FileSystemObject. Zmodyfikowana wersja kodu została przedstawiona na listingu 15.10.
Listing 15.10. Użycie zaimportowanych obiektów COM.
<%@ Page Language="VB" %>
<script runat="server">
sub Page_Load(obj as object, e as eventargs)
dim objFSO as new Scrrun_net.FileSystemObject
dim objFile as Scrrun_net.File
objFile = objFSO.GetFile(Server.MapPath("../rozdzial13/log.txt"))
lblMessage.Text = objFile.Path
end sub
</script>
<html><body>
<asp:Label id="lblMessage" runat="server" />
</body></html>
Teraz mogliśmy ponownie zastosować dobrze znaną metodę tworzenia obiektów. Warto zwrócić uwagę, iż obiekt FileSystemObject należy teraz do przestrzeni nazw Scrrun_net, która dokładnie odpowiada nazwie pliku nowoutworzonej biblioteki. Ta wersja strony będzie działała znacznie efektywniej niż poprzednia, a dodatkowo można wykorzystywać stare obiekty COM w taki sam sposób jak obiekty środowiska .NET.
Notatka
W rzeczywistości środowisko .NET nie konwertuje obiektów COM do postaci komponentów .NET. Zamiast tego tworzy ono warstwę pośrednią generującą konieczne metadane — sam obiekt COM nie jest w żaden sposób zmieniany. Oznacza to, że wirtualna maszyna CLR wciąż nie jest w stanie w pełni wspierać obiektów COM.
To nie jest ASP!
Być może programiści przyzwyczajeni do wcześniejszych wersji technologii ASP są teraz w stanie głębokiego szoku. Jedną z największych zalet ASP.NET jest możliwość tworzenia i wykorzystania bibliotek DLL bez konieczności zatrzymywania i ponownego uruchamiania serwera WWW, przejmowania się konfliktami wersji ani rejestracji bibliotek DLL przy użyciu programu REGSVR32.exe.
Wcześniej, wszystkie obiekty COM musiały być ręcznie rejestrowane, a aby zmiany mogły zostać wykorzystane, konieczne było ponowne uruchomienie serwera. W rzeczywistości, czynniki te uniemożliwiały wykorzystanie wielu rodzajów zdalnej administracji serwerem. Programista lub administrator musiał pracować przy komputerze na którym działał serwer. W ASP.NET nie ma już tej konieczności.
Zgodnie z tym, czego dowiedziałeś się w rozdziale 7, technologia ASP.NET została wyposażona w nowy system konfiguracyjny, który bez trudu może zmienić każda osoba dysponująca odpowiednimi uprawnieniami. Pliki przechowywane w folderze /bin są automatycznie ładowane w czasie działania aplikacji, a wszelkie zmiany można w nich wprowadzać bez żadnych problemów, gdyż serwer nie blokuje dostępu do nich. A zatem, bez zbytniego przejmowania się można je usunąć i zastąpić nową wersją.
Oznacza to również, że proces uruchamiania aplikacji jest znacznie prostszy. Wystarczy skopiować potrzebne pliki do odpowiednich folderów — i najnowsza wersja aplikacji jest gotowa do użycia. Nie trzeba już ponownie uruchamiać serwera lub instruować kogoś jak wykonać cały proces jeśli nie można go przeprowadzić osobiście.
Proces tworzenia bibliotek DLL w środowisku .NET nie zmienił się zbytnio. Wprowadzono w nim jednak kilka innowacji, takich jak wykorzystanie przestrzeni nazw oraz nowy kompilator języka VB.NET. Niemniej jednak przeważająca część procesu projektowania i pisania kodu nie uległa zmianie.
Programiści ASP mogą także być wdzięczni, że wciąż można korzystać z ich bibliotek obiektów COM. ASP.NET daje możliwość posługiwania się tymi obiektami, dzięki znanej już metodzie Server.CreateObject. Metoda ta tworzy obiekty łączone w czasie realizacji programu, podobnie jak we wcześniejszych wersjach technologii ASP. Można także posłużyć się importerem biblioteki typów, aby wzbogacić obiekty COM o pewne wsparcie ze strony wirtualnej maszyny CLR i mechanizmu wczesnego łączenia. Jednak w przeważającej większości przypadków optymalnym rozwiązaniem będzie konwersja istniejących obiektów, dzięki której możliwe będzie wykorzystanie bogatych możliwości środowiska .NET.
Wiele pojęć związanych z tworzeniem i wykorzystaniem obiektów biznesowych nie uległo zmianie w ASP.NET. Zmieniło się środowisko w którym obiekty te są tworzone i wykorzystywane, jednak dzięki temu możliwe było wprowadzenie kilku niezwykle pożądanych usprawnień w procesie implementacji obiektów biznesowych.
Należy pamiętać, że obiekty biznesowe tworzone w środowisku .NET nie są zgodne z wcześniejszymi wersjami technologii ASP. Oznacza to, że nie można wykorzystać obiektu Database z listingu 15.1, w klasycznych stronach ASP. Nie można zapominać, że obiekty .NET są kompilowane do postaci kodu pośredniego MSIL, który z kolei musi zostać skompilowany do właściwego kodu maszynowego przez wirtualną maszynę CLR. Jednak tradycyjne aplikacje ASP nie mogą korzystać z wirtualnej maszyny CLR i nie są w stanie zrozumieć kodu pośredniego MSIL. Oznacza to, że nie będą one w stanie korzystać z nowych obiektów tworzonych w środowisku .NET.
Konkretnie rzecz biorąc proces ten polega na konwersji typów danych, przekazywanych pomiędzy granicami dwóch różnych procesów.
2 Część I ♦ Podstawy obsługi systemu WhizBang (Nagłówek strony)
2 Dokument2
Sens zdania musiał zostać zmieniony, gdyż w tłumaczeniu pomijane są rozdziały oryginału pt. „Week x in Review”.
Akapity w oryginale książki, na listingiem 15.1 są chyba nieco powtórzone i niezbyt sensowne.
UWAGA!! Notatka została pominięta, gdyż mowa w niej o rozdziale dodatkowym „Week 2 in Review”, który nie jest tłumaczony (zgodnie ze strategią dla tej książki).