Architektura .NET oraz ASP.NET

Transkrypt

Architektura .NET oraz ASP.NET
Prof. dr. Hab. Inż. Włodzimierz Khadzhynov
Programowanie w środowisku .NET
1
Spis treści
Architektura .NET oraz ASP.NET ............................................................................................. 4
Kompilacja aplikacji ASP.NET ............................................................................................. 5
Oddzielanie kodu od treści ..................................................................................................... 9
Importowanie przestrzeni nazw ........................................................................................... 12
Obrabianie zdarzeń na stronach ASP.NET ......................................................................... 12
Mechanizm delegacji zdarzeń. ......................................................................................... 14
Formularze internetowe............................................................................................................ 21
Formularzy HTML oraz formularzy WWW ........................................................................ 21
Kontrolki HTML .................................................................................................................. 25
Bezpieczeństwo w ASP.NET ................................................................................................... 28
Podstawowe zagadnienia bezpieczeństwa aplikacji WWW ................................................ 28
Uwierzytelnianie .............................................................................................................. 29
Uwierzytelnianie przez Windows. ................................................................................... 30
Uwierzytelnianie za pośrednictwem formularza . ............................................................ 33
Uwierzytelnianie przy użyciu usługi Passport ................................................................. 39
Personalizacja. .................................................................................................................. 42
Model dostawców w ASP.NET ............................................................................................... 44
Wstawianie użytkowników .................................................................................................. 49
Korzystanie z kontrolki WWW CreateUserWizard ......................................................... 49
Tworzenie strony powitalnej ............................................................................................ 50
Tworzenie strony logowania ............................................................................................ 51
Konfigurowanie aplikacji ASP.NET ........................................................................................ 51
Dostęp do baz danych za pomocą ADO.NET .......................................................................... 52
Otwarcie połączenia z baza danych ..................................................................................... 55
Dodawanie danych przy pomocy ADO.NET ....................................................................... 56
Modyfikacja danych przy pomocy ADO.NET..................................................................... 57
Usuwanie danych przy pomocy ADO.NET ......................................................................... 58
Czytanie danych z bazy danych za pomocą ADO.NET oraz obiektów klasy DataReader. 59
Wykorzystanie parametrów w instrukcjach SQL ................................................................. 60
Wykorzystanie zapamiętanych procedur.............................................................................. 60
Dostęp do danych przy wykorzystaniu obiektów klasy DataSet ......................................... 62
Filtracja i sortowanie danych w klasie DataTable................................................................ 66
Wykorzystanie klas DataRelation ........................................................................................ 70
Struktura klas obiektu DataAdapter ..................................................................................... 71
Wykorzystanie transakcji w ADO.NET ............................................................................... 72
Aktualizacja źródeł danych w DOTNET ............................................................................. 74
Blokowanie pesymistyczne. ............................................................................................. 76
Blokowanie optymistyczne .............................................................................................. 79
Użycie XML ............................................................................................................................. 80
Czytanie danych XML ......................................................................................................... 81
Zapis danych XML............................................................................................................... 82
Walidacja plików XML ........................................................................................................ 83
Związki pomiędzy obiektami klas DataSet oraz Xml .......................................................... 84
Wykorzystanie obiektowego modelu dokumentu XML dla czytania danych ..................... 85
Modyfikacja danych w obiektowym modelu dokumentów XML ....................................... 87
Wiązanie danych ...................................................................................................................... 88
2
Wiązanie danych z kontrolką Repeater oraz obiektem DataReader .................................... 90
Wykorzystanie kontrolki REPEATER oraz obiektu DataReader dla formowania listy
wypunktowanej. ................................................................................................................... 93
Wiązanie danych z kontrolką DropDownList ...................................................................... 95
Wykorzystanie kontrolki DataList ....................................................................................... 96
Edytowanie wyświetlanych pozycji w kontrolce DataList .............................................. 98
Przykład stworzenia aplikacji z kontrolką DataList w środowisku MS Visual Studio. . 101
Wykorzystanie kontrolki DataGrid. ................................................................................... 113
Odwzorowanie kolumn w kontrolce DataGrid .............................................................. 114
Edytowanie danych w kontrolce DataGrid .................................................................... 116
Wykorzystanie szablonów w kontrolce DataGrid .......................................................... 119
Sortowanie danych w kontrolce DataGrid ..................................................................... 122
Podział na strony wyświetlanych danych w obiekcie DataGrid .................................... 124
Kontrolki walidacyjne ASP.NET ....................................................................................... 129
Opracowanie interfejsów graficznych (GUI) za dopomogą stron wzorcowych. ................... 129
Tworzenie interfejsu graficznego za dopomogą tematów i skórek. ....................................... 135
Opracowanie mechanizmów nawigacji witryn WWW ......................................................... 141
Tworzenie serwisów WWW .................................................................................................. 145
Technologia AJAX w ASP.NET. ........................................................................................... 150
Technologia Web Parts w ASP.NET ..................................................................................... 162
3
Architektura .NET oraz ASP.NET
Technologia DOTNET (oraz ASP.NET) udostępniają projektantom następne nowe funkcji i
możliwości w porównaniu z innymi technologiami :
 Tworzenie skompilowanego kodu programowego na poziomie serwera;
 Metodykę „code-behind” – kodu fonu , przeznaczonej dla oddzielenia logiki serwera
od klienckiego formularza;
 Model wiązania danych;
 Instalację aplikacji za dopomogą „ xcopy”;
 Walidację formularzy na poziomie klienta i serwera;
 Unifikację języków programowania;
 Unifikację narzędzie do projektowania aplikacji;
 Jedyny sposób debugowania aplikacji Web i aplikacji lokalnych.
Zasadniczym podejściem w technologii MS .NET jest koncepcja projektowania obiektowego.
Microsoft .NET to jest środowisko do stworzenia oprogramowania, które jest niezależnym od
platformy systemów operacyjnych i sprzętu oraz które pozwoli opracowywać, kompilować,
testować oraz uruchamiać programy wykorzystujące swoje części napisane w różnych
językach oprogramowania.
Microsoft .NET jest zbudowana jako architektura otwarta, która może być wykorzystana do
stworzenia aplikacji Windows lub WWW.
ASP.NET to jest kolekcja klas platformy Microsoft .NET, którzy są przeznaczone do
obsługiwania poleceń protokołu HTTP oraz do realizacji aplikacji WWW.
Praktyczną realizacją Microsoft .NET dla konkretnego systemu operacyjnego jest platforma
.NET Framework. Platforma .NET Framework jest nadbudową do systemu operacyjnego.
Struktura komponentów tej platformy jest pokazana na rys.1.
The .NET Framework Components
Visual
Basic
C++
C#
Perl
Python
…
XML Web Services
User Interface
ASP.NET
ADO.NET and XML
.NET Framework Class Library
Common Language Runtime
Message
Queuing
COM+
(Transactions, Partitions,
Object Pooling)
IIS
WMI
Win32
Rys.1
4
.NET Framework realizuje zadania wirtualnego systemu operacyjnego, który na dolnym
poziomie jest oparty na konkretnym systemu Win32 oraz na górnym - z systemami
oprogramowania oraz aplikacjami którzy odpowiadają standardom Common Language
Specification (CLS). Na rys.1 są pokazane poziomy i komponenty .NET Framework:
 Win32 – system operacyjny , gdzie istnieje .NET Framework (Windows 2000, XP, itp.).
 Aplication Services (Usługi aplikacji) - zbiór usług systemu operacyjnego, którzy mogą
być wywołane przez odpowiednie klasy w bibliotece klas .NET Framework. Głównymi
usługami aplikacji są: komponenty COM+, usługi kolejkowania (Message Queuing), IIS
oraz instrumenty Windows (Windows Management Instrumentation).
 CLR –Common Language Runtime (Wspólne Środowisko Uruchomieniowe). CLR to jest
maszyna wirtualna, która upraszcza instalowanie aplikacji poleceniem xcopy, zapewni
trwałe oraz bezpiecznie środowisko do uruchomienia, wspólny system typów, nadzór nad
wykonywaniem aplikacji, kompilacja kodu aplikacji na 1 i 2 etapie, optymalizacja
kodu(różne języki programowania), automatyczne zarządzanie pamięcią, debugowanie,
bardzo szeroki zestaw przestrzeni nazw(bibliotek).
 .NET Framework Class Library (Biblioteka klas .NET Framework) – to są serwisy, którzy
mogą być wykorzystane dla projektowania aplikacji. Te klasy pozwalają zrealizować
dostęp do różnego rodzaju funkcji systemowych (dostęp do baz danych, wykorzystanie
kontrolek itp.) zgodnie z zasadami projektowania obiektowego. Wszystkie aplikacji .NET
(WWW, Windows), którzy mogą być stworzone w różnych językach oprogramowania,
wykorzystają te same klasy. Użytkownik ma możliwość stworzenia własnych klas.
 ADO.NET - to jest następna wersja technologii ADO dostępu do baz danych. Zawiera
nowe możliwości skutecznego dostępu do danych bez konieczności stałego połączenia ze
źródłem danych. ADO.NET wykorzysta standard XML dla dostępu i przesyłania danych.
 ASP.NET – to jest środowisko projektowania WWW aplikacji w specyfikacjach CLR. Te
aplikacji mogą być uruchamiane przez interfejsy użytkownika (User Interface) lub istnieć
w postaci WEB Serwisu (XML Web Services).
 User Interfaces (Interfejs użytkownika) zawiera: Web Forms (formularzy) - popierają
protokół HTTP, Windows Forms - wykorzystają funkcji interfejsów Windows.
 XML Web Services - usługi WWW, mogą być wykorzystane przez różne aplikacji
WWW w Internecie. . NET Framework zawiera narzędzie do projektowania i
rozpowszechnienia tych usług.
 Języki oprogramowania - muszą być kompatybilnymi z CLS(Common Language
Specification). Ponieważ maszyna wirtualna CLR powinna zapewniać prawidłową
współpracę wszystkich części aplikacji, zdefiniowano podstawowy podzbiór cech, które
musi mieć każdy język programowania. W przeciwnym razie obiekty napisane w różnych
językach nie mogłyby współdziałać. Podzbiór ten jest zdefiniowany w CLS.
Kompilacja aplikacji ASP.NET
Kompilacja aplikacji .NET ma dwa niezależne etapy (rys.2).
5
C#
VB.NET
C++.NET
Inne
1 etap kompilacji
Kod pośredni kompilacji:
Metadane +MSIL
2 etap kompilacji
Win 2000
WinXP
Linux
Inne
Rys.2
Język kompatybilny z platformą .NET nazywa się językiem zarządzalnym. Kod aplikacji
napisanej w dowolnym języku zarządzalnym jest najpierw przekształcany na język pośredni
MSIL(Microsoft Intermediate Language). Kod aplikacji w języku MSIL jest niezależny od
języka wysokiego poziomu i nie zależy od systemu operacyjnego. Plik pośredni MSIL to jest
plik w formacie tekstowym który zawiera metadane. Metadane są to definicje klas,
właściwości, metod, a kod MSIL zawiera ich implementację.
Drugi etap kompilacji polega na skompilowaniu kodu w języku MSIL na instrukcję języka
maszynowego procesora głównego oraz wybranego systemu operacyjnego oraz pozwala na
uruchomienie napisanej aplikacji w wybranym środowisku. Kompilatory uczestniczące w 2
etapie kompilacji określane są skrótem JIT (Just-in-time – na czas). Dokładniej trzeba ich
byłoby nazywać kompilatorami na kod procesora (JIC just-in-code). Na rys. 3 jest pokazana
kolejność etapów kompilacji.
Rys.3
W środowisku CLR został zdefiniowany Wspólny system typów – CTS(Common Type
System). Jest to zestaw typów, jakie mogą zostać standardowo zaimplementowane podczas
deklaracji metadanych i informacji zapisanych w kodzie MSIL – wspólny dla wszystkich
języków zarządzanych. To dlatego kod MSIL jest niezależny od języka programowania
aplikacji.
6
(Demonstracja -> D:\2310B\Media\2310B_01A001.htm)
Schemat etapów kompilacji jest pokazany na rys. 4.
Kompilacja
Program
Kod pośredni MSIL
i metadane
CLR Common
Language
Runtime
Aplikacja
Rys. 4
Rezultatem każdego z tych etapów kompilacji są komponenty (podzespoły) .NET (assembly)
w postaci plików dll, zawierające kody pośrednie MSIL lub kody maszynowe. Komponent
.NET (podzespół) jest to podstawowa jednostka, która może być wielokrotnie używaną przez
CLR. W technologii .NET CLR realizuje w pewnym stopniu funkcji maszyny wirtualnej
Java. Komponenty dll zawierają inne pliki, na przykład strony ASP.NET, obrazy czy pliki
źródłowe VB.NET. Komponent .Net (podzespół) zawiera wewnętrzny manifest, który
stanowi metadanowy opis kodu i zasobów umieszczonych „wewnątrz” komponentu. Wspólne
Środowisko Uruchomieniowe CLR może wykonać tylko kod umieszczony w komponencie
dll. Dlatego nawet strony ASP.NET są umieszczane w komponentach dll tworzonych
dynamiczne, kiedy pojawia się żądanie danej strony. Kod komponentu zawiera w tym
przypadku skompilowany kod programu, który trzeba realizować na serwerze oraz statyczne
elementy HTML.
Wszystkie klasy, którzy są wykorzystane ASP.NET są magazynowane w następnych
bibliotekach komponentów dll:
 Systemowych,
 Globalny archiwum komponentów (GAC – Global assembly cache),
 Lokalny archiwum komponentów.
Wszystkie potrzebne klasy muszą być załadowane przez Proces roboczy ASP.NET
(Workprocess ASP.NET). Na rys. 5 jest pokazany przykład tego procesu oraz przestrzeń
pamięci aplikacji ASP.NET z różnymi modułami komponentów.
Biblioteka komponentów systemowych jest ustalona uruchamianiu usługi .NET. Biblioteka
zawiera klasy systemowe niezbędne dla aplikacji ASP.NET.
Globalny archiwum komponentów GAC zawiera biblioteki dll użytkownika, którzy mogą być
dostępne wszystkim aplikacjom ASP.NET na tym komputerze.
Lokalny archiwum komponentów jest dostępny tylko dla jednej aplikacji.
7
Polecenie HTTP:
GET /foo/foo.aspx
Odpowiedz HTTP:
HTTP/ Hello World!
WorkProcess ASP.NET
AppDomain
Komponenty Systemowe:
System.web.dll
System.data.dll
mscorsvr.dll
Komponenty GAC:
myGACutil.dll
...
Komponenty lokalne:
myPage.dll
...
Rys.5
Główne zalety wykorzystania tego modelu kompilacji:
 Prędkość aplikacji ASP.NET w porównaniu z innymi technologiami wymagającymi
interpretację kodu stron(ASP, PHP).
 Oddzielne strony są skompilowane w klasy, każda strona ma odpowiedni kod
programowy. Dla debagowania stron mogą być wykorzystane te same narzędzie, co i do
debagowania kodu programowego. Błędy na stronach są odwzorowane jako błędy
kompilacji klas programowych. To oznaczy, że większa część błędów będzie odszukana
na etapie kompilacji.
Przykład strony dla demonstracji odczytania typów stworzonych dla strony klas jest pokazany
w listingu hello.aspx. (uruchom: http://localhost/aspnet/hello.ASPX)
Listing hello.aspx
<%@ Page Language = "VB" %>
<script runat = "server">
sub Page_Load (obj as object, e as eventargs)
lblMessage.Text = "hello! To jest ASP.Net!"
end sub
</script>
<HTML>
<%
8
Response.Output.Write ("<p> My Page type is: {0}</p>", _
Me.GetType())
Response.Output.Write ("<p> My Page Base type is: {0}</p>", _
Me.GetType().BaseType)
%>
<BODY>
<asp:Label id="lblMessage" runat="server"/>
</BODY>
</HTML>
Typem każdej strony jest typ ASP.ImieStrony_aspx (imię pliku z symbolem „.” zamienionym
na symbol „_”). Klasą bazową dla każdej strony jest klasa System.Web.UI.Page. Każda
strona ASP.NET jest odziedziczona od tej klasy. Klasa Page zawiera obiekty Application,
Session , Cache oraz właściwości i metody. Właściwościami klasy Page są: Response,
Request oraz Server. To oznaczy, że istnieje kompatybilność że wcześniejszą wersją ASP.
Kod źródłowy strony ASP.NET może zawierać znaczniki HTML, kody scenariuszy w tagach
<%..%> lub w znacznikach <script runat = server> ...</script>. Te kody będą rozmieszczone
w definicji odpowiedniej klasy oraz w funkcjach klasy, którzy będą wywołane przy
zdarzeniach.
W listingu 1 zostało wykorzystane wyrażenie: Response.Output.Write ( format As String,
arg0 as Object), gdzie Format As String zawiera sposób formatowania następnego argumentu
arg0, który musi być typem Object. Pozycja {0} w łańcuchu znaków string pierwszego
argumentu wyznacza miejsce drukowania następnego parametru, który będzie przekształcony
do typu string.
Oddzielanie kodu od treści
Wcześniej stworzone przez Microsoft środowisko czasu wykonywania ASP w ramach
serwera IIS umożliwiało programistom opracowanie programów, które dynamiczne
konstruowały strony oferowany przez IIS. Strona ASP zawierała w sobie połączenie
statycznego kodu HTML i kodu skryptowego. Gdy jakiś klient żądał strony ASP, serwer IIS
znajdował stronę ASP i uaktywniał procesor ASP. Procesor ASP wczytywał stronę i kopiował
jej elementy HTML w postaci niezmienionej do strony wynikowej. Oprócz tego procesor
ASP interpretował elementy skryptowe ujęte w znaczniki „<%...%>” . Kod skryptowy
wykonywał program, którego wynikiem były ciągi kodów HTML. Ciągi te były wstawiane
przez procesor ASP do miejsc, w których na stronie ASP znajdowały się elementy skryptowe,
zastępując je. Środowisko ASP jest stosunkowo proste w użyciu dla prostych zadań.
Wraz z rosnącymi żądaniami użytkowników
powstają wymagania do łatwości
programowania oraz do sprawności działania aplikacji. Wadą ASP jest obecność kodu typu
„spagetti” , gdy w tym samym pliku są przyplątane skrypty oraz znaczniki HTML. ASP.NET
oferuje znaczną poprawę w tym zakresie. ASP.NET wygląda tak jak oryginalne ASP i
większość starego kodu można przenieść na nową platformę bez zmian lub z niewielkimi
zmianami. Ale wewnętrzne ASP.NET został kompletnie przebudowany tak, by mógł
korzystać ze środowiska obiektowego .NET Framework. ASP.NET pozwoli oddzielić kod
HTML od logiki programu przy użyciu techniki zwanej kodem schowanym (code-behind).
Zamiast łączyć znaczniki HTML z kodem, można umieszczać kod w osobnym pliku, do
którego strona ASPX odwołuje się za pomocą odsyłaczy.
Każda strona .ASPX musi dziedziczyć klasę PAGE, ale plik .ASPX nie musi być
bezpośrednią klasą potomną od klasy PAGE. Ten plik może tylko dziedziczyć po tej klasie.
To oznaczy, że można stworzyć pewną klasę pośrednią, dziedziczącą po klasie PAGE i
zażądać, aby plik ImieStrony.aspx (klasy ASP.ImieStrony_aspx) dziedziczył po tej klasie
9
pośredniej. Ta nowa klasa pośrednia może udostępniać dowolne możliwości funkcjonalne,
które będą dostępne dla klas a plikami źródłowymi .ASPX (rys.6).
System.Web.UI.Page
Klasa pośrednia:
Public Class mypr.CodeBehind1 Inherits Page
…
End Class
Klasa dynamiczna utworzona na podstawie pliku
.ASPX : ASP.CodeBehind1_aspx:
<% Page Language =”vb” Inherits =
"mypr.CodeBehind1" Codebehind
="mypr.CodeBehind1.vb" %>
<html> <body> …</body></html>
Rys. 6
Ta technologia pozwoli rozpatrywać oddzielnie kody logiki biznesowej od kodów prezentacji
stron HTML. Do klasy pośredniej można dodawać różne metody logiki
biznesowej,
obrabiania zdarzeń, pola i struktury danych. Wszystko to będzie odziedziczone przez klasą
pliku ImieStrony.aspx. Plik ImieStrony.aspx nie będzie obciążony liniami kodów. Dla
wyznaczenia klasy pośredniej w pliku .aspx musi być odpowiednia dyrektywa Page. W
środowisku MS VisualStudio ta dyrektywa jest wygenerowana w sposób automatyczny i
zawiera słowa kluczowe : Language, Inherits, Codebehind. Przykład dyrektywy Page jest
pokazany na rys.4. Słowo kluczowe Codebehind jest zrozumiale tylko dla środowiska MS
VisualStudio oraz wyznaczy plik źródłowy klasy pośredniej. Standard ASP.NET nie zawiera
tego formatu, zamiast Codebehind jest wykorzystywane słowo kluczowe src. Przykład
dyrektywy dla wyznaczenia pliku z kodem klasy pośredniej w standardzie ASP.NET jest
pokazany w następnym kodzie:
<!- Codebehind1.aspx ->
<%@ Page Language =”VB” src = CodeBehind1.aspx.vb”
Inherits myproj.CodeBehind1 %>
<!- … ->
Plik, który jest wyznaczony przez słowo kluczowe src będzie odkompilowany w oddzielny
moduł strony i rozmieszczony w katalogu /bin projektu.
Istnieją następne możliwości implementacji kodu programowego do stron ASPX:
 Kod mieszany (Mixed code) – gdy na tej samej stronie kod programowy logiki
biznesowej jest przemieszany z kodem HTML logiki prezentacyjnej w znacznikach
<%...%>. Ten sposób jest bardzo zły, nie pozwoli w sposób skuteczny opracować logikę
biznesową oraz logikę prezentacyjną. Wykorzysta ten sposób w technologii ASP.
10


Kod sekcji skryptowych (Inline code) – gdy na tej samej stronie kod logiki biznesowej
jest zawarty w sekcji <SCRIPT>...</SCRIPT>. W tym przypadku istnieją w tym samym
pliku sekcji kodu HTML oraz sekcji kodu programu (Rys. 7)
Kod oddzielony od treści (Code-behind). Ten sposób jest rozpatrzony wyżej oraz
wykorzysta się w MS VS.
Writing Inline Code

Code and content in the same file

Different sections in the file for code and HTML
<HTML>
<HTML>
<asp:Button
<asp:Button id="btn"
id="btn" runat="server"/>
runat="server"/>
...
...
</HTML>
</HTML>
<SCRIPT
<SCRIPT Language="vb"
Language="vb" runat="server">
runat="server">
Sub
Sub btn_Click(s
btn_Click(s As
As Object,
Object, ee As
As EventArgs)
EventArgs)
Handles
Handles btn.Click
btn.Click
...
...
End
End Sub
Sub
</SCRIPT>
</SCRIPT>
Rys. 7
Technologia ASP.NET pozwoli wykorzystać dowolny sposób implementacji kodu. W
przypadkach Mixed code oraz Inline code wszystkie kody są na tej samej stronie ASPX. W
przypadku Code-behind code mamy do czynienia dba pliki (Rys.8). Zaletą sposobu Codebehind code jest możliwość niezależnego projektowania interfejsu użytkownika i logiki
biznesowej.
Rys.8
11
Importowanie przestrzeni nazw
Przestrzeń nazw platformy .NET to jest zbiór prototypów wyznaczonych obiektów klas. Na
przykład klasy bazowe , którzy przeznaczone dla funkcjonowania aplikacji w środowisku
CLR są zgromadzone w przestrzeni nazw System. W tej przestrzeni są rózne serwisy dla
realizacji operacji wejścia – wyjścia, bezpieczeństwa, dostępu do danych oraz inne. Dla
dostępu do więcej wąskich przestrzeń nazw można wykorzystać np. następne referencję:
System.Web lub System.Data. Dostęp do obiektów w przestrzeniach nazw można uzyskać na
stronach typu Mixed Code ( z kodem HTML oraz skryptami ) przez dyrektywę Import, np:
<% Import Namespace = „System.Drawing” %>
W tym przypadku w skryptach zdefiniowanych w znacznikach <script>...<script/> na tej
stronie będą dostępne klasy z kolekcji System.Drawing. Dyrektywa Import informuje
kompilator że trzeba wykorzystać kolekcję klas System.Drawing .
Domyślnie do każdej strony ASP.NET automatyczne importowane są następujące
przestrzenie nazw:
 System
 System.Collection
 System.IO
 System.Web
 System.Web.UI
 System.Web.UI.HtmlControls
 Syste.Web.UI.WebControls
Przestrzenie te są częścią ASP.NET. Nie trzeba ich importować w sposób jawny ani też nigdy
nie będą widoczne żadne polecenia stosowane do ich importowania. Przy stworzeniu
projektów w Visual Studio Net do każdego projektu są dopasowane następne przestrzenie
nazw: System, System.Data, System.Drawing, System.Web, System.XML.
Na stronach typu Code-behind code w klasach programowych przestrzenie nazw mogą być
wyznaczone przez słowa kluczowe Imports:
Imports System.Xml
Aby korzystać z obiektów, nie trzeba importować przestrzeni nazw tego obiektu. Można
wykorzystać pełną nazwę tego obiektu, która zawiera nazwę przestrzeni nazw, np:
...
Dim reader As System.Xml.XmlTextReader
…
reader = New System.Xml.XmlTextReader(Server.MapPath("books.xml"))
…
Obrabianie zdarzeń na stronach ASP.NET
Zdarzenia są sposobem na to, by umożliwić klasie wysyłanie sygnału wskazującego, że zaszła
określona sytuacja o pewnym znaczeniu. Zdarzenia są najczęściej stosowane w interfejsach
użytkownika zbudowanych z formularzy WWW (lub Windows), gdzie sygnalizują pro
klikniecie przycisku, wprowadzenie znaków z klawiatury lub inne czynności.
Zdarzenia mogą również zostać użyte do sygnalizowania innych ważnych wydarzeń, które nie
mają nic wspólnego z interfejsem użytkownika, takich jak zmiana stanu jakiegoś obiektu w
programie.
Obecność klasy pośredniej dla stron ASPX pozwoli definiować w tej klasie metody do
obrabiania zdarzeń. Technologia ASP.NET zawiera dwa poziomy obrabiania zdarzeń:
 Poziom aplikacji
 Poziom strony.
12
Metody do obrabiania zdarzeń na poziomie aplikacji muszą być zdefiniowane w pliku
Global.asa. Metody dla obrabiania zdarzeń na poziomie strony mogą być odziedziczone z klas
nadrzędnych lub wyznaczone w klasie tej samej strony. Klasa bazowa PAGE dziedziczy od
klasy Control cztery zdarzenia:
 Init
 Load
 PreRender
 Unload.
Dla każdego zdarzenia jest wyznaczona metoda wirtualna do obrabiania tego zdarzenia.
Warunki inicjacji tych zdarzeń są następujące:
 Init będzie inicjowane do prezentacji kontrolek strony. Te zdarzenie trzeba wykorzystać
dla inicjacji procesów prezentacji kontrolek oraz dla wyznaczenia procedur obrabiania
przyszłych zdarzeń. Inicjalizacja kontrolek zdarza się do momentu obrazowania kontrolki
na ekranie, dlatego nie można odwołać się do właściwości kontrolek w tym zdarzeniu.
 Load zachodzi, kiedy zostali stworzone wszystkie kontrolki strony oraz rozpoczyna się
ładowanie strony do przeglądarki. Kiedy wynika się te zdarzenie wszystkie kontrolki są
stworzone oraz obrazowane. Do właściwości tych kontrolek można zrealizować dostęp w
tym zdarzeniu.
 PreRender jest inicjowane po realizacji wszystkich zdarzeń do formowania klientem pliku
HTML.
 Unload jest sformowane,
kiedy strona zakończy się swoje istnienie i będzie
unieruchomiona .
W listingu event0.aspx.vb (projekt events.event0) jest pokazany przykład kodu klasy
pośredniej do testowania zdarzeń strony. Kod strony jest pokazany w listingu event0.aspx.
Listing event0.aspx.vb .
Public Class event0
Inherits System.Web.UI.Page
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Init
Trace.Write("Page_Init Event Handler", "Teraz jestem w Page_Init
event handler!")
End Sub
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Trace.Write("Page_Load Event Handler", "Teraz jestem w Page_Load
event handler!")
End Sub
Private Sub Page_PreRender(ByVal sender As Object, ByVal e
System.EventArgs) Handles MyBase.PreRender
Trace.Write("Page_PreRender
Event
Handler",
"Teraz
jestem
Page_PreRender event handler!")
End Sub
As
Private
Sub
Page_Unload(ByVal
sender
As
Object,
ByVal
e
System.EventArgs) Handles MyBase.Unload
Trace.Write("Page_Unload
Event
Handler",
"Teraz
jestem
Page_Unload event handler!")
End Sub
End Class
As
w
w
Kod strony event0.aspx zawiera tylko jedną dyrektywę Page:
Listing listingu event0.asp.
13
<%@
Page
Trace="True"
CodeBehind="event0.aspx.vb"
AutoEventWireup="false" Inherits="events.event0" %>
<!- demonstracja zdarzen strony PAGE->
Language="vb"
Za dopomogą atrybutu Trace=”True” jest wyznaczona możliwość trasowania.
Właściwość AutoEventWireup="true" ustali format Visual Basic 6.0 do specyfikacji
wygenerowanych procedur zdarzeń. Przy
AutoEventWireup="false" jest odłączony
mechanizm poszukiwania funkcji obrabiania zdarzeń w czasie działania kodu aplikacji tym
przypadku aplikacja działa szybsze, dlatego że nie trzeba szukać jakie metody są skojarzone z
konkretnymi zdarzeniami. Dla trybu AutoEventWireup="false" są niezbędne wcześniejsze
rejestracje procedur obrabiania zdarzeń przez słowo kluczowe „Handles”.
Te procedury mogą być skojarzone z odpowiednimi zdarzeniami w następne sposoby:
 Po domyśleniu
 Przez mechanizm delegacji zdarzeń (klas delegatów zdarzeń)
 Przez definicję w sposób jawny ( w kodzie HTML do prezentacji kontrolek)
Sposób ustalenia zdarzeń po domyśleniu jest wykorzystany w listingach 3,4. Nazwy procedur
obrabiania zdarzeń ustalone zgodnie z wzorcem: ImięObiekta_TypZdarzenia. Sposób po
domyśleniu realizuje mechanizm delegacji w sposób niejawny.
Mechanizm delegacji zdarzeń.
Delegat to jest specjalny typ klasy, który przechowuje referencję do metod. Definicja delegata
zawiera prototyp typu funkcji(metody), która może być w delegacie zapisana. Klasa delegata
oznaczana jest słowem kluczowym „delegate”. Wskazuje on rodzaje metod, które mogą
posłużyć do utworzenia jego instancji, ale nie określa nazwy żadnej konkretnej metody, lecz
jedynie typy argumentów i typ wartości zwracanej przez metodę. Może to być metoda zwykła
lub metoda statyczna.
Jeśli tylko sygnatura metody odpowiada sygnaturze zdefiniowanej przez delegat, może ona
zostać skojarzona z tym delegatem, podobne jest z parametrami danej metody. Umożliwia to
programowe modyfikowanie metod, a także dodawanie nowych metod do istniejących już
klas.
Delegaty mogą być również przekazywane jako parametry, co pozwoli realizować wywołania
zwrotne (callback). Wywołanie zwrotne występuje wówczas, gdy metoda otrzymuje jako
parametr delegata wskazującego na pewną metodę, którą należy wielokrotnie, cyklicznie
wywoływać. Dla przykładu, można przedstawić pewną długo pracującą metodę okresowo
wywołującą inną metodę , która uaktualnia pasek postępu pierwszej metody.
Przykład definicji klasy delegatów jest pokazany niżej:
public delegate void MessagePrintDelegate(string msg);
private delegate int GetCountDelegate(Person obj1, Person obj2);
protected delegate void LongRunningDelegate(MessagePrintDelegate
mpCallBack);
Instancja delegata może być utworzona jako zwykła klasa do której jest przypisana konkretna
metoda(może to być metoda zwykła lub statyczna), która musi odpowiadać sygnaturze
delegata. Istnije tylko jedno wymaganie: funkcja musi odpowiadać prototypowi użytemu
podczas deklaracji delegata, np.:
MessagePrintDelegate mpDel = new MessagePrintDelegate(PrintMessage);
GetCountDelegate gcd = new GetCountDelegate(GetCount);
LongRunningDelegate lrd = new LongRunningDelegate(LongRunningMethod);
Przykład wykorzystania delegatów w C# dla realizacji wywołania zwrotnego jest pokazany
w listingu DelegateSimple.Program.cs.
14
1. using System;
2. using System.Collections.Generic;
3. using System.Text;
4. namespace DelegateSimple
5. {
6. class Program
7. {
8. public delegate void MessagePrintDelegate(string msg);
9. private delegate int GetCountDelegate(Person obj1, Person obj2);
10.
protected delegate void
LongRunningDelegate(MessagePrintDelegate mpCallBack);
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
static void Main(string[] args)
{
MessagePrintDelegate mpDel = new
MessagePrintDelegate(PrintMessage);
GetCountDelegate gcd = new GetCountDelegate(GetCount);
int count = gcd(new Person(), new Person());
Console.WriteLine("Otrzymano wartość: {0}", count);
LongRunningDelegate lrd = new
LongRunningDelegate(LongRunningMethod);
lrd(mpDel);
Console.ReadLine();
}
static void LongRunningMethod(MessagePrintDelegate mpd)
{
for (int i = 0; i < 99; i++)
{
if (i % 10 == 0)
{
mpd(string.Format("Praca w toku... Wykonano {0}%", i));
}
}
}
33.
34.
35.
36.
37.
38.
static int GetCount(object obj1, object obj2)
{
// wykonaj jakieś operacje
Random rnd = new Random();
return rnd.Next();
}
39.
40.
41.
42.
43.
44.
static void PrintMessage(string msg)
{
Console.WriteLine("[{0}] {1}",
DateTime.Now.ToShortTimeString(), msg);
}
}
45.
46.
47.
class Person
{
}
48.
49.
50.
51.
class Contact : Person
{
}
}
15
Kowariancja i kontrawariancja
Kowariancja odnosi się do zdolności metody delegata do zwracania pochodnych typów
danych. Przykład. Załóżmy , że mamy delegata zdefiniowanego w następny sposób:
delegate Person GetPersonDelegate();
wówczas kowariancja umożliwia poprawnie wykonanie się poniższych linii kodów:
GetPersonDelegate gpd = new GetPersonDelegate (GetPerson);
GetPersonDelegate gpd = new GetPersonDelegate (GetContact);
Kontrawariancja dotyczy zdolności metody delegata do pobierania jako parametrów obiektów
klas pochodnych.
Większość kontrolek serwerowych mogą generować zdarzenia na serwerze. Przykładem
generacji zdarzenia jest przycisk typu „BUTTON”. Po kliknięciu tego przycisku zawartość
formy strony ASP (wyznaczoną znacznikami <form ...</form HTML) przekazuje się do
serwera za dopomogą metody POST. Kontrolki w odpowiedzi na poczynania użytkownika
uruchamiają zdarzenia. Musi być stworzona w głównej klasie strony ASPX funkcja
obsługująca
zdarzenie.
Po
domyśleniu
to
jest
metoda
o
nazwie
<Nazwa_kontrolki>_<Nazwa_zdarzenia>. W przypadku kontrolki z indeksem BUTTON1 to
jest metoda Button1_Click. Visual Studio.NET tworzy automatyczne funkcji dla obrabiania
zdarzeń . Gdy kontrolka uruchamia zdarzenie, mechanizm zdarzeń CLR szuka funkcji obsługi
cechującej się odpowiedniej nazwą. Mechanizm zdarzeń .NET pozwoli w sposób
dynamiczny ustalić związki pomiędzy obiektem , który jest źródłem zdarzenia oraz obiektem,
który musi otrzymać komunikat pro zdarzenie. Dla realizacji tego mechanizmu są
przeznaczona klasa specjalna – „delegate”. Delegat to jest obiekt specjalny, który pozwoli
źródłu zdarzenia połączyć się z funkcją obrabiania tego zdarzenia. W odróżnieniu od innych
klas delegat zawiera tylko sygnaturę metody realizującą zdarzenie.
Funkcję obsługi można dodać dynamicznie w czasie wywołania kodu za pomocą specjalnej
funkcji Visual Basica AddHandler oraz operatora AddressOf. Format tego operatora
następny: AddressOf procedurename
Operator AddressOf tworzy obiekt klasy „delegate”, który odwoła do funkcji
„procedurename”.
Przykład wyznaczenia procedur do obrabiania zdarzeń przez mechanizm delegatów jest
pokazany w events.event1.vb.
Listing events.event1.vb
Public Class event1
Inherits System.Web.UI.Page
Protected Overrides Sub OnInit(ByVal e As EventArgs)
AddHandler Me.Load, New EventHandler(AddressOf MyLoadHandler)
AddHandler Me.PreRender, New EventHandler(AddressOf
MyPreRenderHandler)
End Sub
Protected Sub MyLoadHandler(ByVal src As Object, ByVal e As EventArgs)
Trace.Write("Load Event Handler", "Teraz jestem w MyLoadHandler
event handler!")
End Sub
Protected Sub MyPreRenderHandler(ByVal src As Object, ByVal e As
EventArgs)
Trace.Write("PreRender Event Handler", "Teraz jestem w
MyPreRenderHandler event handler!")
End Sub
End Class
W tym listingu w klasie event1 funkcja OnInit jest przesłaniana metoda klasy macierzystej. W
tej metodzie w sposób dynamiczny są wyznaczone nowe procedury dla obrabiania zdarzeń
Load oraz PreRender.
16
Wyznaczenie procedur zostało zrealizowano za dopomogą specjalnej funkcji Visual Basica
AddHandler:
AddHandler Me.Load, New EventHandler(AddressOf MyLoadHandler)
Pierwszym argumentem tej funkcji jest obiekt klasy „Event” klasy macierzystej: Me.Load.
Drugim argumentem jest obiekt klasy „EventHandler”. Parametrem wejściowym konstruktora
EventHandler() jest obiekt klasy „delegat” który został stworzony przez operator AddressOf.
Operator AddressOf ma tylko jeden parametr: sygnaturę procedury: MyLoadHandler.
Przykład dynamicznego stworzenia funkcji obsługiwania zdarzeń jest pokazany w listingu
Events2.aspx.vb (projekt events).
Listing Events2.aspx.vb
1. Public Class WebForm1 Inherits System.Web.UI.Page
2.
Sub TestEvents()
3.
Dim Obj As New Class1
4. ' Associate an event handler with an event.
5.
AddHandler Obj.Ev_Event, AddressOf myevents
6.
Obj.CauseSomeEvent()
' Ask the object to raise an event.
7.
End Sub
8.
Sub myevents()
9. ' This procedure handles events raised by the object Obj.
10.
Me.Label1.Text = "EventHandler złapal zdarzenie (event)."
11.
End Sub
12.
Public Class Class1
13.
Public Event Ev_Event()
' Declare an event.
14.
Sub CauseSomeEvent()
15.
RaiseEvent Ev_Event()
' Raise an event.
16.
End Sub
17.
End Class
18.
Protected WithEvents Label1 As System.Web.UI.WebControls.Label
19.
…
20.
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Init
21.
InitializeComponent()
22.
End Sub
23.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
24.
25.
Me.TestEvents()
End Sub
End Class
26.
W liniach 12 – 17 definiowana klasa „Class1”. W tej klasie w linii 13 zostało zadeklarowane
zdarzenie Ev_Event() bez parametrów wejściowych. Funkcja realizująca bezpośrednio te
zdarzenie nie jest zdefiniowana w tej klasie oraz będzie dołączona w sposób dynamiczny.
Klasa „Class1” zawiera metodę CauseSomeEvent(), która zawiera jedną linię 15. Operator
RaiseEvent wywoła zdarzenie Ev_Event(). W liniach 2 – 7 zdefiniowana metoda
TestEvents() klasy WebForm1. W linii 3 tej metody zdefiniowany obiekt Obj który
prezentuje klasę Class1. W linii 5 za dopomogą instrukcji :
AddHandler Obj.Ev_Event, AddressOf myevents
zdarzeniu Ev_Event została przypisana realna funkcja z nazwą „myevents”, realizująca tę
zdarzenie. Sama funkcja myevents zdefiniowana w liniach 8-11.
Format operatora RaiseEvent następny:
RaiseEvent eventname[( argumentlist )]
Przykład wyznaczenia zdarzeń obiektów kontrolek serwerowych jest pokazany w listingu
Events3.aspx.vb ( projekt events).
Listing Events3.aspx.vb
17
1.
2.
3.
4.
Public Class WebForm2
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
<System.Diagnostics.DebuggerStepThrough()> Private Sub
InitializeComponent() End Sub
5. Protected WithEvents _MyButton As System.Web.UI.WebControls.Button
6. Protected WithEvents _message As System.Web.UI.WebControls.Label
7. Protected WithEvents Button1 As System.Web.UI.WebControls.Button
8. Protected WithEvents Label1 As System.Web.UI.WebControls.Label
9. Private designerPlaceholderDeclaration As System.Object
10.
Protected Sub OnClickMyButton(ByVal src As Object, ByVal e As
EventArgs)
11.
_message.Text = "You clicked the button ""Click me""!"
12.
End Sub
13.
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Init
14.
InitializeComponent()
15.
AddHandler _MyButton.Click, AddressOf OnClickMyButton
16.
End Sub
17.
#End Region
18.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
19.
End Sub
20.
Private Sub Button1_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Button1.Click
21.
Label1.Text = "to button!"
22.
End Sub
23.
Private Sub _MyButton_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles _MyButton.Click
24.
_message.Text = "Tego komunikatu nie będzie !"
25.
End Sub
26.
End Class
W liniach 5 -8 zdefiniowane obiekty kontrolek serwerowych razem ze słowami kluczowymi
Protected WithEvents co oznaczy, że kontrolki mogą generować zdarzenia. W linii 15
sekcji Page_Init do zdarzenia „Click” kontrolki _MyButton jest przypisana metoda
OnClickMyButton . Przy kliknięciu _MyButton będzie komunikat "You clicked the
button ""Click me""!". Kod funkcji tej kontrolki po domyśleniu w liniach 23-25 nigdy
nie będzie uruchamiany.
Dla obrabiania zdarzeń kontrolki Button1 wykorzysta się po domyśleniu metoda
Button1_Click (linii 20-22).
Większość kontrolek serwerowych mogą generować zdarzenia na serwerze. Przykładem
generacji zdarzenia jest przycisk typu „BUTTON”. Po kliknięciu tego przycisku zawartość
formy strony ASP (wyznaczoną znacznikami <form ...</form HTML) przekazuje się do
serwera za dopomogą metody POST. Kontrolki w odpowiedzi na poczynania użytkownika
uruchamiają zdarzenia. Musi być stworzona w głównej klasie strony ASPX funkcja
obsługująca
zdarzenie.
Po
domyśleniu
to
jest
metoda
o
nazwie
<Nazwa_kontrolki>_<Nazwa_zdarzenia>. W przypadku kontrolki z indeksem BUTTON1 to
jest metoda Button1_Click. Visual Studio.NET tworzy automatyczne funkcji dla obrabiania
zdarzeń . Gdy kontrolka uruchamia zdarzenie, mechanizm zdarzeń CLR szuka funkcji obsługi
cechującej się odpowiedniej nazwą. Mechanizm zdarzeń .NET pozwoli w sposób
dynamiczny ustalić związki pomiędzy obiektem , który jest źródłem zdarzenia oraz obiektem,
który musi otrzymać komunikat pro zdarzenie. Dla realizacji tego mechanizmu są
przeznaczona klasa specjalna – „delegate”. Delegat to jest obiekt specjalny, który pozwoli
źródłu zdarzenia połączyć się z funkcją obrabiania tego zdarzenia. W odróżnieniu od innych
klas delegat zawiera tylko sygnaturę metody realizującą zdarzenie.
18
Funkcję obsługi można dodać dynamicznie w czasie wywołania kodu za pomocą specjalnej
funkcji Visual Basica AddHandler oraz operatora AddressOf. Format tego operatora
następny: AddressOf procedurename
Operator AddressOf tworzy obiekt klasy „delegate”, który odwoła do funkcji
„procedurename”.
Przykład wyznaczenia procedur do obrabiania zdarzeń przez mechanizm delegatów jest
pokazany w events.event1.vb.
Listing events.event1.vb
Public Class event1
Inherits System.Web.UI.Page
Protected Overrides Sub OnInit(ByVal e As EventArgs)
AddHandler Me.Load, New EventHandler(AddressOf MyLoadHandler)
AddHandler Me.PreRender, New EventHandler(AddressOf
MyPreRenderHandler)
End Sub
Protected Sub MyLoadHandler(ByVal src As Object, ByVal e As EventArgs)
Trace.Write("Load Event Handler", "Teraz jestem w MyLoadHandler
event handler!")
End Sub
Protected Sub MyPreRenderHandler(ByVal src As Object, ByVal e As
EventArgs)
Trace.Write("PreRender Event Handler", "Teraz jestem w
MyPreRenderHandler event handler!")
End Sub
End Class
W tym listingu w klasie event1 funkcja OnInit jest przesłaniana metoda klasy macierzystej. W
tej metodzie w sposób dynamiczny są wyznaczone nowe procedury dla obrabiania zdarzeń
Load oraz PreRender.
Wyznaczenie procedur zostało zrealizowano za dopomogą specjalnej funkcji Visual Basica
AddHandler:
AddHandler Me.Load, New EventHandler(AddressOf MyLoadHandler)
Pierwszym argumentem tej funkcji jest obiekt klasy „Event” klasy macierzystej: Me.Load.
Drugim argumentem jest obiekt klasy „EventHandler”. Parametrem wejściowym konstruktora
EventHandler() jest obiekt klasy „delegat” który został stworzony przez operator AddressOf.
Operator AddressOf ma tylko jeden parametr: sygnaturę procedury: MyLoadHandler.
Przykład dynamicznego stworzenia funkcji obsługiwania zdarzeń jest pokazany w listingu
Events2.aspx.vb (projekt events).
Listing Events2.aspx.vb
27.
28.
29.
30.
31.
32.
Public Class WebForm1 Inherits System.Web.UI.Page
Sub TestEvents()
Dim Obj As New Class1
' Associate an event handler with an event.
AddHandler Obj.Ev_Event, AddressOf myevents
Obj.CauseSomeEvent()
' Ask the object to raise an
event.
33.
End Sub
34.
Sub myevents()
35.
' This procedure handles events raised by the object Obj.
36.
Me.Label1.Text = "EventHandler złapal zdarzenie (event)."
37.
End Sub
38.
Public Class Class1
39.
Public Event Ev_Event()
' Declare an event.
40.
Sub CauseSomeEvent()
41.
RaiseEvent Ev_Event()
' Raise an event.
42.
End Sub
19
43.
44.
45.
46.
End Class
Protected WithEvents Label1 As System.Web.UI.WebControls.Label
…
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Init
47.
InitializeComponent()
48.
End Sub
49.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
50.
51.
Me.TestEvents()
End Sub
End Class
52.
W liniach 12 – 17 definiowana klasa „Class1”. W tej klasie w linii 13 zostało zadeklarowane
zdarzenie Ev_Event() bez parametrów wejściowych. Funkcja realizująca bezpośrednio te
zdarzenie nie jest zdefiniowana w tej klasie oraz będzie dołączona w sposób dynamiczny.
Klasa „Class1” zawiera metodę CauseSomeEvent(), która zawiera jedną linię 15. Operator
RaiseEvent wywoła zdarzenie Ev_Event(). W liniach 2 – 7 zdefiniowana metoda
TestEvents() klasy WebForm1. W linii 3 tej metody zdefiniowany obiekt Obj który
prezentuje klasę Class1. W linii 5 za dopomogą instrukcji :
AddHandler Obj.Ev_Event, AddressOf myevents
zdarzeniu Ev_Event została przypisana realna funkcja z nazwą „myevents”, realizująca tę
zdarzenie. Sama funkcja myevents zdefiniowana w liniach 8-11.
Format operatora RaiseEvent następny:
RaiseEvent eventname[( argumentlist )]
Przykład wyznaczenia zdarzeń obiektów kontrolek serwerowych jest pokazany w listingu
Events3.aspx.vb ( projekt events).
Listing Events3.aspx.vb
27.
28.
29.
30.
Public Class WebForm2
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
<System.Diagnostics.DebuggerStepThrough()> Private Sub
InitializeComponent() End Sub
31.
Protected WithEvents _MyButton As
System.Web.UI.WebControls.Button
32.
Protected WithEvents _message As
System.Web.UI.WebControls.Label
33.
Protected WithEvents Button1 As
System.Web.UI.WebControls.Button
34.
Protected WithEvents Label1 As System.Web.UI.WebControls.Label
35.
Private designerPlaceholderDeclaration As System.Object
36.
Protected Sub OnClickMyButton(ByVal src As Object, ByVal e As
EventArgs)
37.
_message.Text = "You clicked the button ""Click me""!"
38.
End Sub
39.
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Init
40.
InitializeComponent()
41.
AddHandler _MyButton.Click, AddressOf OnClickMyButton
42.
End Sub
43.
#End Region
44.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
45.
End Sub
46.
Private Sub Button1_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Button1.Click
47.
Label1.Text = "to button!"
20
48.
49.
End Sub
Private Sub _MyButton_Click(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles _MyButton.Click
50.
_message.Text = "Tego komunikatu nie będzie !"
51.
End Sub
52.
End Class
W liniach 5 -8 zdefiniowane obiekty kontrolek serwerowych razem ze słowami kluczowymi
Protected WithEvents co oznaczy, że kontrolki mogą generować zdarzenia. W linii 15
sekcji Page_Init do zdarzenia „Click” kontrolki _MyButton jest przypisana metoda
OnClickMyButton . Przy kliknięciu _MyButton będzie komunikat "You clicked the
button ""Click me""!". Kod funkcji tej kontrolki po domyśleniu w liniach 23-25 nigdy
nie będzie uruchamiany.
Dla obrabiania zdarzeń kontrolki Button1 wykorzysta się po domyśleniu metoda
Button1_Click (linii 20-22).
Formularze internetowe
Formularzy HTML oraz formularzy WWW
Formularze zawierają komponenty interfejsu graficznego oraz pozwalają realizować pracę
interaktywną klienta. Formularz ma dwa zadania: pierwszym z nich jest zebranie informacji
podanych przez użytkownika, a drugim – przesłanie ich do wyznaczonej strony na serwerze w
celu ich przetworzenia. Dzięki zastosowaniu formularzy, aplikacja internetowa może zażądać
otrzymania danych od użytkownika i wykorzystać te dane do podejmowania decyzji. W
standardzie HTML wszystkie elementy interfejsu graficznego (pola formularzy) muszą być
rozlokowane w znacznikach <form> , </form> na stronie klienta.
Technologia ASP.NET pozwoli wykorzystać dwa typy formularzy:
 Formularzy HTML
 Formularzy WWW.
Formularzy HTML składają część standardu HTML oraz są wykorzystane w tradycyjnym
ASP. One zawierają elementy, które tworzą interfejs użytkownika z elementów HTML. To
są: przyciski, pola wyboru, listy i odsyłacze prezentowane odpowiednimi znacznikami
HTML. W aplikacjach internetowych użytkownicy wprowadzają dane do tych elementów i
wysyłają formularz, który przesyła dane do serwera. Zasady działania formularzy HTML są
pokazane na rys.9.
Te formularzy mogą przesyłać dane tylko do strony z ustalonym adresem w polu ACTION
znacznika <form>. Protokół przesyłania musi być wyznaczony w polu METHOD przez
wartości GET lub POST. W tym przypadku serwer komunikuje się z klientem tylko za
pomocą komunikatów żądań i przekazywania formularzy.
Przy wykorzystaniu formularzy HTML serwer nie ma informacji o interfejsie użytkownika
oraz o rodzaje danych, które zostaną przesłane. Serwer ma tylko dane, które prześle
formularz. Po przesłaniu danych wprowadzonych za pomocą formularza, są one usuwane
przez serwer i serwer zapomina pro komunikację. Przy wykorzystaniu formularzy HTML
niema możliwości sterować zdarzeniami oddzielnych elementów formularza. Modeli tego
typu komunikacji pomiędzy klientem a serwerem nazywają modelami „żądanie –
odpowiedź”.
21
Klient
1.Tworzy elementy
interfejsu
2.Wprowadzanie
danych i wysyłanie
formularza
3.Wysylanie
danych
Serwer
4 Otrzymuje
dane
Rys.9
Technologia ASP.NET zawiera nowy typ formularzy –WWW (WEB).Formularze WWW
składają się z elementów, które tworzą interfejs użytkownika oraz mogą być przesłane w obu
kierunkach: „klient-serwer” oraz „serwer-klient. Wyznaczenie adresy strony docelowej nie
jest potrzebne. Jeśli nie jest określone pole ACTION formularz powraca do samego siebie.
Ten rodzaj formularzy nazywa się formularzami zwrotnymi (postback forms). Dla analizy
danych można na tej samej stronie umieścić fragmenty kodów, które zajmą się danymi
wejściowymi. Współczesne technologii projektowania oprogramowania obiektowego
potrzebują obecności gotowych komponentów lub kapsułek funkcjonalnych
do
projektowania aplikacji. W formularzach WWW te komponenty nazywają się kontrolkami i
są wykorzystywane do tworzenia interfejsów graficznych, realizacji dostępu do baz danych,
walidacji danych, prezentacji danych na stronach WWW, nawigacji na stronach WWW,
przesyłania komunikatów poczty elektronicznej,
generacji obiektów programowych
użytkownika itp. Technologia ASP.NET zawiera następne typy kontrolek: kontrolki HTML,
kontrolki WWW, kontrolki walidacji danych, kontrolki użytkownika. Każda z tych kontrolek
jest prezentowana odpowiednim kodem HTML wewnątrz znaczników „<form>...</form>”
na stronie klienta. Proces komunikacji serwera z klientem przy wykorzystaniu formularzy
WWW jest pokazany na rys. 10. Skoro klient zarządza danej strony, ASP.NET przekształca
kontrolki WWW w kod HTML, który jest zrozumiały dla przeglądarki i przesyła go do
klienta. Za pomocą skryptów po stronie klienta, generowanych automatycznie przez
ASP.NET, elementy tych kontrolek alarmują serwer za każdym razem, kiedy wystąpi
odpowiednie wydarzenie, na przykład zostanie naciśnięty przycisk. W ten sposób serwer jest
stale informowany o tym, co dzieje się na komputerze klienta.
22
Serwer:
1.Tworzenie
elementów
interfejsu użytkownika
2.Wysylanie
elementów w
kodzie HTML
Klient:
4.Wysłanie
danych z
powrotem do
serwera
3. Wypelnianie
formularza i wysłanie
Rys.10
Ten sposób komunikacji pomiędzy klientem a serwerem nazywa się modelem sterowania
zdarzeniami (event driven model). Technologia ASP.NET do przysyłania danych w obu
kierunkach wykorzysta model żądanie-odpowiedź na dolnym poziomie komunikacji.
Nadbudowanie modelu sterowanego zdarzeniami nad modelem
„żądanie-odpowiedź”
umożliwia tworzenie aplikacji na zasadach programowania obiektowego.
Kody definicji Formularzy WWW przypominają tradycyjne formularzy HTML. Różnica
polega na tym, że formularzy WWW oprócz elementów HTML mogą zawierać kontrolki
które są utworzone na serwerze oraz maja stałe połączenie przez kanał wirtualny z klientem.
Kontrolki serwerowe (server controls) mają kod programowy na serwerze oraz odpowiedni
kod HTML i skrypty na stronie klienta. Przy wykorzystaniu formularzy z kontrolkami
serwerowymi serwer ma wszystkie informacje na temat interfejsu użytkownika i jakich
danych oczekuje. Na serwerze są tworzone serwerowe obiekty sterujące, przedstawiające
składniki interfejsu użytkownika. W przeciwieństwie do składników formularzy HTML,
obiekty te są sterowalne – można manipulować ich atrybutami, zdarzeniami, metodami.
Kod strony formularza WWW zawiera następne części: elementy interfejsu użytkownika,
które są wyświetlane oraz związane z nimi funkcje. Przykład formularza jest pokazany w
wydruk0503.aspx (Projekt formularzy, wydruk0503.aspx).
Listing wydruk0503.aspx.
1. <%@
Page
Language="vb"
CodeBehind="wydruk0503.aspx.vb"
AutoEventWireup="false" Inherits="formularze.wydruk0503" %>
2. <HTML>
3. <script runat="server">
4. sub Button1_Click(obj as object, e as eventargs)
5.
Label1.Text="You clicked <b>" & obj.Text & "</b>"
6. end sub
7. </script>
8. <body>
9.
<font size="5">ASP.NET jest platforma Microsoft </font>
23
10.
<hr>
11.
<p>
12.
<form runat="server">
13.
<asp:Button id="Button1" runat="server" Text="Przycisk1"
14.
OnClick="Button1_Click" />
15.
<p>
16.
<asp:Label id="Label1" runat="server" />
17.
</form>
18.
</p>
19. </body>
20. </HTML>
Każdy obiekt serwerowy musi mieć ustalony parametr runat="server". Kontrolki serwerowe
muszą być skojarzone z zdarzeniami przez OnClick="kontrolka_zdarzenie" lub przez
delegację zdarzenia w klasie. Zdarzenia są wysyłane do serwera na dwa sposoby: natychmiast
po wystąpieniu lub zbiorowo w jednym komunikacie. Natychmiastowy sposób można
zrealizować za dopomogą właściwości kontrolki AutoPostBack=true(kiedy kontrolka zawiera
tą właściwość, np. kontrolka textbox). W przypadku sposobu zbiorowego wszystkie
zdarzenia są buforowane(cached), to znaczy że zapisane są na komputerze-kliencie, dopóki
użytkownik nie zdecyduje się przesłać dane. Po przesyłaniu formularza do serwera zostanie
zrealizowana następna kolejność zdarzeń :
1. Page_Init. W tym zdarzeniu są stworzone oraz inicjowane kontrolki serwerowe.
2. Generowanie zdarzenia Page_Load.
3. Obsługa zdarzeń z bufora (na przykład Textbox1_Changed) oraz zdarzeń którzy
spowodowali przesłanie formularza(na przykład Button1_Click). Jeśli wystąpi kilka
zdarzeń jednocześnie, to przetwarzane są bez określonej kolejności.
4. Generowanie zdarzenia Page_Unload.
Cykl życiowy obrabiania zdarzeń na stronie jest pokazany na rys.11.
Understanding the Page Event Life Cycle
Page_Init
Page_Init
Control events
Page_Load
Page_Load
Change Events
Textbox1_Changed
Textbox1_Changed
Action Events
Button1_Click
Button1_Click
Page_Unload
Page_Unload
Page is disposed
Rys.11
W środowisku ASP.NET zapamiętany są widoki stanu każdej kontrolki. Tak samo dzieje się
dla danych wprowadzonych przez użytkownika. Jeśli użytkownik wpisze swoje dane
w
pole tekstowym, okaże się, że wprowadzony tekst pozostaje w tym polu po wysłaniu z
powrotem formularza do klienta. Tradycyjne formularzy HTML nie mają tej możliwości,
wprowadzane dane są usuwane po każdym przesłaniu. W technologii ASP.NET osiągnięto to
przez wystawienie na wyjściu zawartości ukrytych pól formularza HTML za każdym razem,
kiedy formularz otrzyma polecenie runat="server". Na stronie klienta w tym przypadku jest
sformowany znacznik typu „hiden” oraz zmienna z nazwiskiem „_viewstate” która zawiera
24
ciąg znaków. Zmienna „_viewstate” jest przeznaczona do przechowywania zawartości
kontrolek.
W ASP.NET nie jest konieczne ponowne wprowadzanie przez użytkownika wartości
serwerowych elementów sterujących (kontrolek) po przesłaniu formularza, ponieważ jest to
robione automatycznie w systemie. Obiekt PAGE zawiera atrybut IsPostBack, który
wskazuje, czy dany formularz został wysłany. Kiedy IsPostBack=false formularz nie był
jeszcze żadnego razu przesłany do klienta. Zasady wykorzystania atrybutu IsPostBack są
pokazane na rys.12.
Handling Page Postback Events

Page_Load fires on every request

Initialize controls

Use Page.IsPostBack to execute conditional logic
Private
Private Sub
Sub Page_Load(ByVal
Page_Load(ByVal ss As
As System.Object,
System.Object, __
ByVal
ByVal ee As
As System.EventArgs)
System.EventArgs) __
Handles
Handles MyBase.Load
MyBase.Load
If
If Not
Not Page.IsPostBack
Page.IsPostBack Then
Then
'executes
'executes only
only on
on initial
initial page
page load
load
End
End If
If
'this
'this code
code executes
executes on
on every
every request
request
End
End Sub
Sub

Page.IsPostBack prevents reloading for each postback
Rys.12
Formularze internetowe zapisują widok stanu każdej z kontrolek za pomocą ukrytych pól
formularza.
(Demonstracja postback forms: D:\2310B\Media\2310B_05A002.htm)
Kontrolki HTML
Elementy kodu HTML są elementami przetwarzanymi wyłącznie po stronie klienta. Na
przykład, fragment kodu <input type= „text” id=”Pole1” value=”Kowalski”> jest
przetwarzany do postaci pola tekstowego na przeglądarce.
Kontrolki HTML są elementami przetwarzanymi po stronie serwera. Są to obiekty tworzone
na serwerze, posiadające atrybuty, metody i zdarzenia, które można odpowiednio obsłużyć.
Służą do generowania kodu HTML przesyłanego do przeglądarki. Kontrolki HTML są łatwe
do tworzenia ze zwykłego kodu HTML: do każdego elementu HTML dodaje się atrybut
runat=”server”. Każdy z elementów HTML na stronie klienta w tym przypadku ma
odpowiadający mu serwerowy obiekt sterujący HTML.
(Pokazać formularze.upr.aspx, HTML, Synchronize Dokument online)
Klasy wszystkich kontrolek HTML są dziedziczone od klasy HtmlControl. Klasa
HtmlControl jest dziedziczona od klas Control oraz Object. Przykład hierarchii klas dla
kontrolek HtmlButton, HtmlInputText oraz HtmlInputButton są pokazane na rys. 13-15.
System.Object
System.Web.UI.Control
System.Web.UI.HtmlControls.HtmlControl
System.Web.UI.HtmlControls.HtmlContainerControl
System.Web.UI.HtmlControls.HtmlButton
Rys.13
System.Object
System.Web.UI.Control
25
System.Web.UI.HtmlControls.HtmlControl
System.Web.UI.HtmlControls.HtmlInputControl
System.Web.UI.HtmlControls.HtmlInputText
Rys14
System.Object
System.Web.UI.Control
System.Web.UI.HtmlControls.HtmlControl
System.Web.UI.HtmlControls.HtmlInputControl
System.Web.UI.HtmlControls.HtmlInputButton
Rys.15
Po domyśleniu komponenty panelu HTML w Visual Studio .NET nie są obiektami
serwerowymi. Żeby przekształcić komponent HTML do postaci kontrolki HTML trzeba na
panelu projektu w menu kontekstowym kontrolki wybrać opcję „Run As Server Control”. Ta
opcja powoduje wygenerowanie atrybutu „runat = „server”” do definicji kontrolki.
Przykład kodu do manipulowania atrybutami kontrolek HTML jest pokazany w projektu
formularze.wydruk0506.aspx.
<%@ Page Language="vb" AutoEventWireup="false"
Inherits="formularze.wydruk0506" CodeFile="wydruk0506.aspx.vb" %>
<HTML>
<body>
<font size="5">Kontrolki HTML </font>
<hr>
<form method="post" runat="server">
<input type="button" id="Left" runat="server"
Value="Lewy" OnServerClick="Click">
<input type="button" id="Center" runat="server"
Value="Srodkowy" OnServerClick="Click">
<input type="button" id="Right" runat="server"
Value="Prawy" OnServerClick="Click">
<img src="BIKE.GIF" id="rower_image" runat="server"
alt="To jest mój rower!">
<div id="Label1" runat="server">
<p>
To jest tekst przykladowy. Kiedy powyzsze przyciski
zostaly naciSniete obraz przesunie sie dokola
tekstu.
Przyklad ten to przedstawienie obiektów HtmlImage i
HtmlInputButton.
</p>
</div>
</form>
</body>
</HTML>
Namespace formularze
Partial Class wydruk0506
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
26
'This call is required by the Web Form Designer.
<System.Diagnostics.DebuggerStepThrough()> Private Sub
InitializeComponent()
End Sub
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub
Sub Click(ByVal obj As Object, ByVal e As EventArgs)
Select Case obj.Value
Case "Lewy"
rower_image.Align = "left"
Case "Prawy"
rower_image.Align = "right"
Case "Srodkowy"
rower_image.Align = "center"
End Select
Left.Style("Border-Style") = "notset"
Right.Style("Border-Style") = "notset"
Center.Style("Border-Style") = "notset"
obj.Style("Border-Style") = "inset"
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
End Sub
End Class
End Namespace
27
Bezpieczeństwo w ASP.NET
W ASP.NET zostali zrealizowane usługi uwierzytelniania(authentication) oraz autoryzacji
które działają wspólnie z IIS.
Ustawienia mechanizmów bezpieczeństwa, z których będą korzystały aplikacje ASP.NET
umieszcza się m.in. w plikach konfiguracyjnych będącymi dokumentami XML o ściśle
określonej strukturze. Wyróżniamy dwa rodzaje plików konfiguracyjnych ASP.NET:
Znajdujący się w katalogu: %WinDir%\Microsoft.NET\Framework\ <version>\Config
plik Machine.config zawierający ustawienia globalne dla całego serwera. Ze względu na
możliwość popełnienia błędu, który może mieć wpływ na działanie wszystkich aplikacji
zainstalowanych na serwerze, nie jest zalecana bezpośrednia ingerencja w zawartość
tego pliku.
2.
Web.config zawierający konfigurację dla katalogu, w którym się znajduje oraz
podkatalogów. Każda aplikacja ASP.NET musi posiadać przynajmniej jeden plik
Web.config, który jest umieszczony je katalogu głównym. Każdy z podkatalogów
aplikacji może zawierać własny Web.config nadpisujący ustawienia bardziej ogólne. W
momencie dostępu do zasobu ASP.NET określa dla niego konfigurację poprzez
hierarchiczne prześledzenie ustawień.
1.
Podstawowe zagadnienia bezpieczeństwa aplikacji WWW
Protokół zabezpieczania w WWW zawiera następnie kroki (rys.16):
 Identyfikacja(autentyfikacja)
użytkownika czyli uwierzytelnienie. To jest proces
określenia tożsamości użytkownika żądającego dostępu do informacji. Tożsamość
najczęściej jest określana przez nazwę i hasło użytkownika. Dzięki uwierzytelnianiu
można uzyskać pewność, że użytkownik, który przesłał zadanie, jest tym za kogo siebie
podaje. Jeśli system zabezpieczenia nie będzie w stanie określić tożsamość użytkownika
na podstawie przesłanych informacji uwierzytelniających, to proces uwierzytelniania nie
zostanie wykonany poprawnie, a anonimowy użytkownik
nie uzyska dostępu do
zasobów informacyjnych. Jeśli jednak informacje
uwierzytelniające okażą się
poprawne, to użytkownik uzyska dostęp do systemu i zostanie mu przydzielona
odpowiednia „tożsamość”.
 Po określeniu tożsamości użytkownika system sprawdza, do jakich zasobów może on
uzyskać dostęp. Proces ten nazywany jest autoryzacją.
 Personalizacja (impersonation) to jest czynność, która pozwoli procesu ASP.NET być
uruchomianym z identyfikatorem użytkownika oraz z jego uprawnieniem.
Kiedy
użytkownik niema uprawnień dostępu do wyznaczonych resursów, to procesy systemowe
ASP.NET także nie będą miały dostępu do tych resursów.
28
Podanie danych
uwierzytelnienia
Nie
jest
Uwierzytelniony?
Tak
Próba uzyskania
dostępu do
zasobów
Brak dostępu
Nie
Autoryzowany?
Tak
Personalizacja
danej tożsamości
Rys.16
Uwierzytelnianie
Środowisko ASP.NET obsługuje następne cztery różne tryby uwierzytelniania:
1. NONE – nie są używane żadne usługi uwierzytelniania ASP.NET.
2. WINDOWS – standardowe uwierzytelnianie Windows z IIS.
3. FORMS – ASP.NET wymaga, aby wszystkie moduły obsługujące żądania
stron
zawierały cookies wydane przez serwer. Użytkownicy próbujący uzyskać dostęp do
zabezpieczonych stron bez cookie są przekierowywani do strony logowania, która
weryfikuje ich i wydaje cookies.
4. PASSPORT - jest analogiczny trybu FORMS, tylko cookies są wydawane przez
wewnętrzny serwis uwierzytelniania Microsoftu, PASSPORT.
Tryby Uwierzytelniania mogą być ustalone w IIS na stronie „Metody Uwierzytelniania”.
Większość witryn WWW domyślnie zezwala na anonimowy dostęp do swoich zasobów(tryb
NONE). Oznacza to, że każdy, kto dysponuje dostępem do Internetu, może wejść na taką
witrynę i przejrzeć umieszczone na niej strony WWW. Użytkownicy nie muszą być
uwierzytelniani, ich tożsamość nie musi być sprawdzana, a każdy użytkownik dysponuje
możliwością obejrzenia dowolnych plików znajdujących się na witrynie. Ta sytuacja jest
bardzo zła dla realizacji systemów informatycznych z poufnej informacją, np. banki, giełdy,
agencji rządowe.
Tryby uwierzytelnienia są zrealizowane przez providerzy (providers) uwierzytelnienia.
Providerzy to są moduły programowe zawierające kod służący do uwierzytelnienia żądań
przesyłanych przez klienty WWW. Istnieją następne typy providerów:
 Provider dla wewnętrznego uwierzytelnienia Windows za dopomogą managera sieci
lokalnej Windows NT (NTLM – NT LAN Manager).
 Provider dla uwierzytelnienia za dopomogą formularza (forms).
 Provider dla identyfikacji po paszportu (passport).
Działanie providerów jest kontrolowane za pośrednictwem plików konfiguracyjnych
web.config w aplikacji ASP.NET. Ten plik zawiera sekcje do konfigurowania aplikacji. W
tych sekcjach mogą być zadane różne parametry bezpieczeństwa, zwłaszcza: wyznaczony
tryb uwierzytelniania, identyfikatory i hasła użytkowników, sposób szyfrowania haseł itp.
29
Przykład pliku web.config w którym jest zadany tryb uwierzytelniania „Forms” (za
dopomogą formularzy) jest pokazany w listingu :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<authentication mode="Forms"/>
</system.web>
</configuration>
Klient
Serwer
Przegłądarka
IIS
ASP.NET
.NET Framework
Windows NT/2000/XP/...
Rys.17
Ogólny schemat zabezpieczeń ASP.NET jest pokazany na rys. 17. Ten schemat wyznacza
funkcji IIS przy uwierzytelnieniu klientów. Na tym schemacie pokazano, że IIS może
bezpośrednio komunikować z systemem operacyjnym dla uwierzytelniania w trybie
„Windows” lub przekierować proces uwierzytelniania do ASP.NET w innych trybach.
Gdy klient przesyła żądanie dotyczące jakiejś strony ASP.NET, jest ono odbierane przez IIS.
Po odebraniu żądania serwer IIS może uwierzytelnić użytkownika, bądź pozostawić
przeprowadzenie uwierzytelnienia w gestii aplikacji ASP.NET. Jeśli to serwer będzie
uwierzytelniał użytkownika, to jest on w stanie porównać jego dane uwierzytelniające
bezpośrednio z informacjami, jakimi dysponuje system operacyjny.
Uwierzytelnienie przez IIS nie jest wymagane. Przy ustaleniu opcji „Enable anonymous
access” w zakładce „Authentication methods” IIS, serwer IIS będzie tylko przekazywał
wszystkie odbierane żądania bezpośrednio do aplikacji ASP.NET. Każde żądanie klienta w
tym przypadku wykorzysta konto na komputerze serwera „IUSR_MACHINE”, gdzie
MACHINE oznaczy imię komputera serwera.
Strony aplikacji ASP.NET będą
odpowiedzialne za uwierzytelnianie oraz użytkownicy będą traktowane jako anonimowe.
Dostęp anonimowy sprawia, że każdy użytkownik ma prawo pobrać dowolne zasoby
znajdujące się w katalogu. Żeby zrealizować uwierzytelnianie za dopomogą IIS, trzeba
skasować opcję „Enable anonymous access” w zakładce „Authentication methods” IIS.
Uwierzytelnianie przez Windows.
Windows Authentication Provider umożliwia uwierzytelnienie w oparciu o mechanizmy
uwierzytelnienia dostępne w Internet Information Services (IIS). Wszystkie żądania od klienta
do aplikacji ASP.NET zanim dotrą do samej aplikacji muszą przejść w pierwszej kolejności
przez IIS. IIS zawsze – przy każdym wspieranym sposobie uwierzytelnienia – zakłada, że
zestaw credentials zawartych w żądaniu może być zweryfikowany w oparciu o informację
30
zawartą w katalogu użytkowników Windows. Każdy uwierzytelniany użytkownik musi
posiadać konto w katalogu Windows. Jeśli credentials nie odpowiadają istniejącemu i
aktywnemu kontu użytkownika IIS odrzuca żądanie klienta. Z tego względu ten rodzaj
uwierzytelnienia nie jest zalecany dla dużych rozwiązań integrujących różne technologie.
IIS przekazuje do procesu hostującego(roboczego) ASP.NET instancję klasy
WindowsIdentity reprezentującą uwierzytelnionego użytkownika (identity).
Podstawową cechą uwierzytelniania przez Windows jest aktywne uczestnictwo IIS. Przy tym
trybie uwierzytelnienia tożsamości wykorzystywane przez IIS są określane przez konta
użytkowników systemu Windows. Aby wykorzystać ten tryb uwierzytelniania, trzeba ustalić
wyznaczyć typ uwierzytelniania IIS oraz należy umieścić poniższy znacznik w pliku
web.config:
<authentication mode="Windows"/>
Serwer IIS używa trzech różnych typów uwierzytelniania systemu Windows.:
 Basic autentication – uwierzytelnianie podstawowe
 Digest autentication for Windows domain servers – uwierzytelnianie bazujące na
skrótach.
 Integrated Windows Autentication – zintegrowane uwierzytelnianie Windows.
Typ uwierzytelniania może być ustalony przez odpowiednią opcję
w zakładce
„Authentication methods” IIS. Rozpatrzymy szczegółowo te typy uwierzytelniania.
Uwierzytelnianie podstawowe (Basic autentication) - jest najprostszą metodą
uwierzytelniania. Przy żądaniu klienta do aplikacji ASP.NET zostanie zrealizowany protokół
uwierzytelniania, który sformuje okno dialogowe z dodatkowym formularzem do którego
trzeba podać identyfikator i hasło użytkownika. Te dane będą przesyłane na serwer w postaci
zwyczajnego tekstu oraz potem będą porównane z istniejącymi kontami użytkowników
Windows. To nie jest bezpieczny sposób przeprowadzenia uwierzytelniania.
Uwierzytelnianie bazujące na skrótach (Digest autentication for Windows domain servers) działa podobnie jak uwierzytelnianie podstawowe z tym, że zarówno nazwa użytkownika, jak
i hasło są szyfrowane przed wysłaniem. Opcja „Digest autentication for Windows domain
31
servers” w IIS może być dostępna wyłącznie wtedy, gdy serwer jest podłączony do domeny
sieci Windows.
Zintegrowane uwierzytelnianie Windows (Integrated Windows Autentication) nie potrzebuje
okien dialogowych, w których są podane dane uwierzytelniające. Nie jest to konieczne, gdyś
gdy tylko przeglądarka nawiąże kontakt z serwerem, przesyła do niego zaszyfrowaną
informacje, które użytkownik podał przy logowaniu do systemu (to może być dostęp
anonimowy lub przez konta Windows). Po otrzymaniu tych informacji IIS przetwarza je i
sprawdza, czy dany użytkownik powinien uzyskać dostęp do żądanego zasobu.
Zintegrowane uwierzytelnianie systemu Windows używa listów uwierzytelniających
użytkowników, które zostały przedstawione w trakcie ich logowania do systemu Windows.
Okno dialogowe, służące do pobierania
listów
uwierzytelniających, nie zostanie
użytkownikowi wyświetlone, chyba że dane uwierzytelniające, przedstawione w trakcie
logowania do systemu Windows, są nieodpowiednie w stosunku do żądanych zasobów.
Zintegrowane uwierzytelnianie systemu Windows składa się z różnych typów
uwierzytelniania:
NT LAN Manager hasło / odzew oraz Kerberos.
NTLM jest protokołem używanym w platformach Windows oraz domenach. W
przeciwieństwie od protokołu Kerberos NTLM nie obsługuje przekazywanie uprawnień.
Kerberos jest szybszym protokołem od NTLM, chociaż nie tak szybkim jak uwierzytelnianie
podstawowe. Kerberos jest polecany dla wykorzystania w przypadkach, kiedy aplikacja
przewiduje dużą ilość równoczesnych użytkowników lub przekazywanie uprawnień
bezpieczeństwa do SQL Server.
Zarówno uwierzytelnianie bazujące na skrótach, jak i zintegrowane uwierzytelnianie
Windows wymagają, aby użytkownik korzystał się tylko z przeglądarki Microsoft IE.
Uwierzytelnianie przez Windows jest rzadko wykorzystywane w WWW aplikacjach, dlatego
że jest niezbędne ustalenie kont użytkowników na serwerze.
W listingu Webwindows.config jest pokazany plik konfiguracyjny dla demonstracji trybu
„Windows”.
listing Webwindows.config
<?xml version="1.0" encoding="utf-8" ?>
<!-- Dla strony loginwindows.aspx -->
<!-- Ustal "dostep anonimowy" = false ! -->
<configuration>
<system.web>
<authentication mode="Windows" />
</system.web>
</configuration>
W listingu loginwindows.aspx do tego pliku podany kod strony , która wydrukowuje konto
użytkownika Windows oraz typ protokołu uwierzytelniania windows.
Listing loginwindows.aspx :
Public Class loginwindows
Inherits System.Web.UI.Page
...
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles MyBase.Load
'Put user code to initialize the page here
Me.AuthUser.Text = User.Identity.Name
Me.AuthType.Text = User.Identity.AuthenticationType
End Sub
End Class
Dla ustalenia trybu uwierzytelniania Windows trzeba w serwerze IIS dla odpowiedniego
katalogu wirtualnego (w tym przykładzie Authentication) ustalić (patrz rys.17a) :
32
Dostęp anonimowy = false
Uwierzytelnienie podstawowe= true
:
Rys.17a
Uwierzytelnianie za pośrednictwem formularza .
W celu ochrony wybranych stron przed nieuprawnionym dostępem programista może
posłużyć się mechanizmem uwierzytelnienia opartym o własną stronę logowania – własny
formularz (forms-based authentication). Podstawą forms-based authentication jest dostępny w
HTTP mechanizm przekierowywania. Użytkownicy nieuwierzytelnieni przy próbie dostępu
do chronionych zasobów są kierowani do specjalnie wyznaczonej strony zawierającej
formularz umożliwiający wprowadzenie credentials, które następnie są przekazywane do
aplikacji ASP.NET. W przypadku pomyślnego uwierzytelnienia aplikacja wystawia
ciasteczko uwierzytelniające (authentication cookie), które jest kojarzone z sesją
użytkownika, po czym użytkownik jest przekierowywany z powrotem do RETURNURL.
Authentication cookie jest przekazywane do aplikacji w nagłówkach kolejnych żądań HTTP.
Authentication cookie nie musi być utrwalane w przeglądarce.
Technologia ASP.NET daje możliwość realizacji uwierzytelniania nie za pośrednictwem
serwera IIS, lecz tworzonej aplikacji ASP.NET. W aplikacji ASP.NET musi być stworzony
przez projektanta formularz (strona ASP.NET), który służy do podania informacji
uwierzytelniających. Głównym elementem mechanizmu uwierzytelniania w tym przypadku
jest cookie stworzony i skojarzony ze stroną użytkownika w razie skutecznego
uwierzytelniania przez formularz. Mechanizm działania uwierzytelniania przez formularz jest
pokazany na rys.18.
33
IIS
Użytkownik zgłasza żądanie
Użytkownik
uzyskuje prawo
dostępu do zasobu
Tak
Zezwala na przekazanie
żądania
Czy jest
dostępne cookie
autoryzacyjne?
Nie
Przekierowanie do
formularza
logowania
Pobranie danych
uwierzytelniających
Stworzenie cookie
autoryzacyjnego
Tak
Czy jest
uwierzytelniony?
Nie
Brak dostępu
Rys.18
Przebieg procesu uwierzytelniania przy wykorzystaniu formularza zawiera następne
czynności:
1. Klient żąda dostępu do chronionej strony.
2. Jeśli wraz z żądaniem nie zostało przesłane ważne cookie uwierzytelniające, to serwer
przekierowuje przeglądarkę pod adres URL formularza do logowania.
3. Użytkownik podaje dane w formularzu, a następne wysyła go (dane są wysyłane na
serwer metodą POST).
4. Jeśli dane uwierzytelniające są poprawne, to ASP.NET tworzy cookie
uwierzytelniające i przesyła je do klienta.
5. Użytkownik uzyskuje prawo dostępu.
34
35
Po utworzeniu cookie uwierzytelniającego wszystkie żądania przesyłane przez danego
użytkownika będą automatycznie uwierzytelniane aż do momentu zamknięcia przeglądarki i
zakończenia sesji.
Przykład pliku konfiguracyjnego web1.config, który wyznacza tryb uwierzytelnianie przez
formularz jest pokazany w listingu web1.config.
Listing web1.config.
<!-- Dla strony login1.aspx -->
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="AuthCookie" loginUrl="login1.aspx" >
</forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</configuration>
W znaczniku <forms...> są wyznaczone imię cookie (name="AuthCookie") oraz URL
formularza aplikacji (loginUrl="login1.aspx"). W znaczniku <authorization> jest
zabroniony anonimowy dostęp do aplikacji (<deny users="?"/>). Strona login1.aspx musi
zrealizować uwierzytelnianie użytkownika oraz sformować cookie w razie prawidłowych
danych uwierzytelniania. Przykład metody realizującej uwierzytelnianie na stronie
login1.aspx jest pokazany w listingu Login1.aspx.
Listing Login1.aspx.
Private Sub btSubmit_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btSubmit.Click
If tbUserName.Text = "user" And _
tbPassword.Text = "password" Then
' 1-parameter imie cookie, 2-parameter cookie usuwa sie po
zamkniejciu explorera
FormsAuthentication.SetAuthCookie("user", False)
'Response.redirect( Response.Querystring("ReturnUrl") )
Response.Redirect("account.aspx")
Else
lblMessage.Text = "<font color=red>Przykro mi, " & _
"nieprawidlowa nazwa uzytkownik lub haslo!</font><p>"
End If
End Sub
Kod w listingu Login1.aspx analizuje podane przez użytkownika dane. W razie prawidłowych
danych jest wykorzystana klasa statyczna System.Web.Security.FormsAuthentication .
Metoda SetAuthCookie tej klasy formuje cookie z kluczem “user” oraz użytkownik zostaje
przekierowany do strony ("account.aspx"). Metoda SetAuthCookie zawiera drugi
parametr (true/false) który wyznacza czy musi być chroniony cookie po zamknięciu
eksplorera. Przy wartości „True” drugiego parametru cookie będzie chroniony po zamknięciu
przeglądarki i użytkownik nie musi przy ponownym uruchomianiu aplikacji rejestrować się.
W listingu Login1.aspx uwierzytelnianie użytkownika realizuje się w wyrażeniu
IF...Else...End If. ASP.NET ma możliwość realizować logikę uwierzytelniania automatyczne.
Identyfikatory użytkowników i ich hasła muszą być w pliku web.config. ASP.NET sam
sprawdzi odpowiedniość danych wpisanych przez użytkownika z danymi web.config. W
listingu web2.config jest pokazany plik zawierający trzy konta użytkowników.
Listing web2.config:
<!-- Dla strony login2.aspx -->
<configuration>
<system.web>
36
<authentication mode="Forms">
<forms name="AuthCookie" loginUrl="login2.aspx" >
<credentials passwordFormat ="Clear">
<user name="user" password="password"/>
<user name="user1" password="password1"/>
<user name="user2" password="password2"/>
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</configuration>
Sekcja <credentials> zawiera atrybuty użytkowników – name oraz password.
Atrybut
passwordFormat określa sposób szyfrowania danych uwierzytelniających przy ich przesyłaniu
na serwer. Wartość „Clear” oznacza, że dani nie są szyfrowanie. Przykład metody
realizującej uwierzytelnianie dla tego przypadku jest pokazany w listingu login2.aspx . Dla
uwierzytelniania
jest
wykorzystana
metoda
Authenticate(tbUserName.Text,tbPassword.Text)
klasy
statycznej
System.Web.Security.FormsAuthentication . Parametrami wejściowymi tej metody są
dane z kontrolek TextBox odbierających identyfikator i hasło użytkownika.
Listing login2.aspx:
Private Sub btSubmit_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btSubmit.Click
If FormsAuthentication.Authenticate(tbUserName.Text, _
tbPassword.Text) Then
FormsAuthentication.SetAuthCookie(tbUserName.Text, False)
Response.Redirect("account.aspx")
Else
lblMessage.Text = "<font color=red>Przykro mi, " & _
"nieprawidlowa nazwa uzytkownika lub haslo!</font><p>"
End If
End Sub
Klasa System.Web.Security.FormsAuthentication
zawiera
jeszcze
metodę
FormsAuthentication.RedirectFromLoginPage(), która formuje cookie w ten samy
sposób, że i metoda SetAuthCookie ,lecz dodatkowo powoduje także przekierowanie
przeglądarki użytkownika pod adres URL, którego początkowo dotyczyło żądanie. Jeśli w
łańcuchu zapytania nie zostały zapisane informacje o żądanej stronie, to metoda ta przekieruje
przeglądarkę użytkownika na stronę default.aspx. Nie można zatem o niej zapomnieć! Ta
strona musi być w katalogu głównym. Przykład procedury realizującej uwierzytelnianie z
wykorzystaniem tych metod jest pokazany w listingu login_3.aspx. Plik web.config w tym
przypadku jest pokazany w web3.config.
Listing login_3.aspx.
Private Sub btSubmit_Click(ByVal sender As System.Object, ByVal e
System.EventArgs) Handles btSubmit.Click
If FormsAuthentication.Authenticate(tbUserName.Text, _
tbPassword.Text) Then
FormsAuthentication.RedirectFromLoginPage(tbUserName.Text,
False)
Else
lblMessage.Text = "<font color=red>Przykro mi, " & _
"nieprawidlowa nazwa uzytkownika lub haslo!</font><p>"
End If
End Sub
As
Listing web3.config.
<!-- Dla strony login_3.aspx -->
37
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="AuthCookie" loginUrl="login3.aspx" >
<credentials passwordFormat ="Clear">
<user name="user" password="password"/>
<user name="user1" password="password1"/>
<user name="user2" password="password2"/>
</credentials>
</forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</configuration>
Metoda FormsAuthentication.RedirectFromLoginPage, jest przydatna do
przesyłania
użytkowników na stronę, którą chcieli obejrzeć, jednak brakuje jej możliwości wykonania
jakichkolwiek czynności po stworzeniu cookie uwierzytelniającego. To znaczy, ze po linii 5
kodu w listingu 5 będzie uruchamiana inna strona oraz żadna ewentualna instrukcja po linii 5
już nie będzie nigdy aktywna. Istnieje możliwość wygenerować cookie oraz pobrać adres
strony, której początkowo dotyczyło żądanie bez przechodzenia do niej. Te przejście można
zrealizować później po szeregu wyznaczonych czynności. W tym celu trzeba wykorzystać
metodę FormsAuthentication.GetRedirectURL(). Na przykład :
Dim strURL = FormsAuthentication.GetRedirectURL(„user”,false)
Klasa FormsAuthentication udostępnia także inną metodę – GetAuthCookie(). Tworzy ona
obiekt klasy. Ten obiekt nie jest obiektem klasy cookie, lecz tylko zawiera kopię przyszłego
cookie. Sam cookie może być utworzony bezpośrednio przez obiekt HttpCookie. Metoda
GetAuthCookie() ma jednak tę zaletę, iż pozwala na wykonania jakichś czynności po
utworzeniu kopię cookie autoryzacyjnego i przed jego przekazaniem do przeglądarki
użytkownika. Na przykład można dodać do cookie dowolne dodatkowe informacje(termin
ważności cookie itp.). W listingu login4.aspx jest pokazany przykład procedury realizującej
uwierzytelnianie z wykorzystaniem metody GetAuthCookie(). W liniach 3-4 realizuje się
uwierzytelnianie przez metode Authenticate(). W linii 5 został zdefiniowany obiekt klasy
HttpCookie. W liniach 6-7 jest stworzony obiekt klasy HttpCookie który ma nazwisko
identyfikatora użytkownika. W linii 8 dla przyszłego cookie jest ustalony termin ważności. W
linii
9
jest
stworzony
sam
cookie
na
komputerze
użytkownika:Response.Cookies.Add(cookie). W ciągu 2 minut ten cookie będzie ważny,
to znaczy po zamknięciu przeglądarki i powtórnym ją uruchamianiu w ciągu tego terminu nie
będzie potrzebne powtórne logowanie tego użytkownika. ASP.NET dla uwierzytelniania
będzie pobierał dane użytkownika z pliku cookie na komputerze użytkownika.
Listing login4.aspx.
Private Sub btSubmit_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles btSubmit.Click
If FormsAuthentication.Authenticate(tbUserName.Text, _
tbPassword.Text) Then
Dim cookie As HttpCookie
cookie
=
FormsAuthentication.GetAuthCookie(tbUserName.Text,
False)
cookie.Expires = DateTime.Now.AddMinutes(2)
Response.Cookies.Add(cookie)
lblMessage.Text = "<font color=red>Udalo sie!</font><p>"
Else
38
lblMessage.Text = "<font color=red>Przykro mi, " & _
"nieprawidlowa nazwa uzytkownik lub haslo!</font><p>"
End If
End Sub
Klasa FormAuthentification zawiera metodę SignOut, która pozwala na wylogowanie danego
użytkownika. Metoda ta usuwa cookie autoryzacyjne, co sprawia, że przy ponownej próbie
dostępu do chronionych zasobów użytkownik będziesię musiał ponownie zalogować.
Uwierzytelnianie przy użyciu usługi Passport
Passport-based authentication korzysta z Microsoft Passport® – scentralizowanej usługi
uwierzytelnienia dostarczonej przez Microsoft umożliwiającej realizację single sign-on –
jednorazowego uwierzytelniania – w ramach witryn korzystających z tej usługi. Celem usługi
webowej (web service) Microsoft Passport jest dostarczenie jednego wspólnego mechanizmu
uwierzytelnienia dla wszystkich witryn korzystających z usługi Passport. Usługa Microsoft
Passport stanowi odpowiedź na problem tzw. „rozmnażania się” danych uwierzytelniających
(credentials proliferation). Za pomocą jednego zestawu credentials użytkownik może uzyskać
dostęp do wszystkich witryn zarejestrowanych w Microsoft Passport.
Uwierzytelnienie w oparciu o Microsoft Passport wymaga, by:


witryna umożliwiała korzystanie z tej usługi;
osoby korzystające z aplikacji ASP.NET posiadały konta użytkowników w ramach
usługi .NET Passport.
W momencie dostępu do zasobu wymagającego uwierzytelnienia strona, do której
użytkownik chce się dostać wyszukuje ciastka (cookie) Microsoft Passport w nagłówku
żądania HTTP. Podobnie jak ma to miejsce w przypadku forms-based authentication jeśli
strona nie znajdzie ciasteczka Microsoft Passport w nagłówku żądania, użytkownik zostanie
przekierowany do witryny .NET Passport. Po pomyślnym uwierzytelnieniu użytkownik jest z
powrotem przekierowywany do witryny chronionej przez Passport.
Ten tryb uwierzytelniania działa podobnie do uwierzytelniania za pośrednictwem formularzy,
z tą różnicą , iż żadnych możliwości funkcjonalnych nie trzeba implementować samodzielnie.
Podstawą działania obu tych trybów są cookies przechowywanych na komputerach
użytkowników i wykorzystanych przy uwierzytelnianiu. W razie uwierzytelniania w trybie
Passport użytkownik jest kierowany do specjalnej strony logowania Passport, która sama
przesyła prosty formularz dla uzupełniania danymi uwierzytelniania. Formularz ten sprawdza
dane uwierzytelniające użytkownika, porównując je z danymi zgromadzonymi przez usługę
Microsoft „Passport” i określa, czy są one poprawne. Proces Uwierzytelniania przy użyciu
usługi Passport jest pokazany na rys. 19.
39
Przeglądarka
Żądanie strony która
jest uwierzytelniana
przez Passport
Server IIS
Usluga MS Passport
TAK
Czy jest cookie?
NIE
Przekierowanie
klienta do strony
logowania
Pobieranie
identyfikatora i hasła z
ekranu logowania
Farma
serwerów MS
Passport
Przesyłanie danych
do Microsoftu
NIE
Klient ma
Passport?
Dostarczanie cookie
do klienta
Dostęp przegłądarki
do uwierzytelnianej
strony
TAK
Udostępnianie
strony do klienta
Dostęp do strony jest
zabroniony
Rys. 19
Uwierzytelnianie w tym przypadku nie jest bezpośrednio pod kontrolą aplikacji NET, lecz
pod kontrolą usługi Microsoft.
Anonymous Authentication
Konfiguracja aplikacji ASP.NET umożliwia również całkowitą rezygnację z uwierzytelnienia
(anonymous authentication), bądź korzystanie z własnego mechanizmu uwierzytelnienia
dostosowanego do konkretnych potrzeb użytkownika (custom authentication). Przykładem
własnego schematu uwierzytelnienia może być filtr wykorzystujący Internet Server
Application Programming Interface (ISAPI). Nasz własny filtr realizowałby własny
mechanizm uwierzytelnienia oraz przykładowo tworzył obiekt klasy GenericPrincipal.
<configuration>
<system.web>
<authentication mode="None"/>
</system.web>
</configuration>
Oczywiście konfiguracja określająca brak uwierzytelnienia nie przesądza, że nie będzie
wykonywane uwierzytelnienie po stronie IIS – tyle, że aplikacja ASP.NET nie będzie
zainteresowana wykorzystaniem rezultatów tego uwierzytelnienia.
Autoryzacja.
Uwierzytelnianie (identyfikacja) jest procesem określenia tożsamości użytkownika na
podstawie pewnych informacji. Celem autoryzacji jest wyznaczenie resursów (stron aplikacji)
40
do których ma dostęp uwierzytelniany klient. W technologii ASP.NET proces autoryzacji
można wykonać na dwa sposoby:
1. Autoryzacja dostępu do plików
2. Autoryzacja dostępu do adresów URL.
W przypadku pierwszej metody ASP.NET prowadzi interakcję z systemem operacyjnym w
celu powiązania tożsamości użytkownika z informacjami zapisanymi na listach kontroli
dostępu (Access Control List – ACL). Gdy uwierzytelniony użytkownik spróbuje dostęp do
jakiegoś pliku na serwerze WWW, system operacyjny sprawdzi odpowiednie listy kontroli
dostępu, aby określić, czy dany użytkownik lub dana rola ma prawa konieczne do
wyświetlenia zawartości tego pliku. Przy określaniu uprawnień na podstawie ACL,
autoryzacja dostępu do plików współdziała z procesem personalizacji . Wadą tego sposobu są
problemy z ustaleniem praw użytkowników w ACL w przypadku dużej ilości katalogów i
plików aplikacji ASP.NET.
Sposób autoryzacji za dopomogą adresów URL polega na odwzorowaniu tożsamości
użytkownika na foldery znajdujące się w żądanych adresach URL. Przykłady autoryzacji byli
wykorzystane wcześniej w listingach 2,4,6 plików web.config. W tych przykładach był
zabroniony dostęp do wszystkich plików i katalogów użytkowniku anonimowemu. W
znacznikach <allow>... </allow> oraz <deny>…</deny> sekcji <authorization>…
</authorization> są podawane uprawnienia. Przykład pliku konfiguracyjnego z
autoryzacją jest pokazany w web5.config.
Listing web5.config.
<!-- Dla plika login5.aspx -->
<configuration>
<system.web>
<authentication mode="Forms">
<forms name="AuthCookie" loginUrl="login5.aspx" >
<credentials passwordFormat ="Clear">
<user name="user" password="password"/>
<user name="user1" password="password1"/>
<user name="user2" password="password2"/>
<user name="user3" password="password3"/>
</credentials>
</forms>
</authentication>
</system.web>
<location path="account.aspx">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
<location path="folder2">
<system.web>
<authorization>
<allow users ="user3,user2" />
<deny users="*"/>
</authorization>
</system.web>
</location>
</configuration>
W tym pliku dostęp do wszystkich resursów jest wzbroniony użytkownikom anonimowym
oraz w sekcji <credentials>...</credentials> są wyznaczone konta użytkowników,
które mają dostęp do wszystkich plików aplikacji oraz katalogów podrzędnych. Natomiast
dostęp do podrzędnego katalogu folder2 jest udostępniony tylko użytkownikom user2 i user3.
41
Składnia pliku Web.config umożliwia:


określenie ograniczeń w dostępie do zasobów (plików, katalogów) dla
indywidualnych użytkowników, bądź też grup użytkowników (ról), jak również rodzajów
żądań klienckich (GET lub POST);
określenie, czy ograniczenia w dostępie do zasobów dotyczy całości aplikacji, czy też
wybranych zasobów: stron (plików), lub podkatalogów aplikacji.
W trakcie definicji uprawnień można posługiwać się symbolami wieloznacznymi (wildcards):


"*" – oznacza każdego użytkownika, bądź każdą grupę;
"?" – oznacza użytkownika anonimowego.
Personalizacja.
Personalizacja pozwala technologii ASP.NET na wykonywanie przy wykorzystaniu praw
dostępu klienta, którego żądanie jest obsługiwane. Innymi słowy wszystkie procesy w
systemie operacyjnym komputera są skojarzone z tożsamością klienta który został wcześniej
uwierzytelniany. Kiedy ten klient nie ma uprawnień dostępu do wyznaczonych plików, to
aplikacja ASP.NET nie zrealizuje operacji z tymi plikami.
Domyślnie mechanizm personalizacji jest wyłączony. W tym przypadku procesy ASP.NET
będą wykonywane przy użyciu tożsamości IIS. Schemat procesu personalizacji jest pokazany
na rys. 20.
Żądanie
użytkownika
IIS
Czy jest
uwierzytelniony?
NIE
Brak Dostępu do resursu
TAK
ASP.NET
Personalizacja
włączona?
TAK
Aplikacja ASP.NET
przyjmuje tożsamość
użytkownika
NIE
NIE
Aplikacja ASP.NET
przyjmuje tożsamość IIS
Dostęp do resursu jest
dozwolony
Czy listy ACL
zezwalają dostęp?
TAK
Rys.20
Włączanie personalizacji może być zrealizowane w następnym kodzie pliku web.config:
42
configuration>
<system.web>
<identity impersonate = ”true” username=”user” password =”psw”/>
</system.web>
</configuration>
W tym przykładzie wszystkie procesy ASP.NET będą z uprawnieniami użytkownika
„user/psw” oraz informacje o użytkownikach uwierzytelnionych przez IIS są ignorowane.
Możliwość ta jest bardzo przydatna w sytuacjach, gdy wszyscy użytkownicy mają mieć
dokładnie te same uprawnienia. Reguły wyznaczenia uprawnień personalizacji są w
następnej tabeli.
Sposób
zadania Dostęp
anonimowy
jest Dostęp
anonimowy
jest
Personalizacji
dozwolony
wzbroniony
impersonate = ”true”
IUSR_ComputerName
Identyfikator
oddalonego
użytkownika
impersonate = ”false”
Identyfikator procesu IIS
Identyfikator procesu IIS
jest
wyznaczone
konto Wyznaczony w znacznikach Wyznaczony w znacznikach
user/psw w znacznikach <identity>...</identity <identity>...</identity
<identity>...</identity
> identyfikator
> identyfikator
>
Decydującym w wyznaczeniu uprawnień przez plik web.config jest obecność parametrów
oraz password w znacznikach <identity>...</identity> . W przypadku,
kiedy te pola maja wartości obecność pola impersonate = ”true” będzie ustalona
automatyczne.
username
43
Model dostawców w ASP.NET
Dla realizacji połączeń w Internet jest wykorzystywany protokół http, który jest protokołem
bezstanowym. To oznaczy, że serwer nie pamięta stan żadnego klienta, tylko realizuje
polecenia GET/POST. Od klientów – przychodzą żądania, a wysyłane są odpowiedzi. Ten
protokół nie pozwoli odróżnić jednego komunikatu od innego. Serwer po prostu reaguje na
przysyłanie do niego żądania. Dla realizacji trybu bezpołączeniowego w technologii
ASP.NET trzeba mieć możliwość aplikacjom użytkowników na zapamiętywanie kontekstu
pomiędzy żądaniami do serwera podczas całej sesji z aplikacją. Zapamiętywanie kontekstu
oznacza zapisywanie stanu aplikacji w magazynie danych. W ASP.NET są kilka metod dla
zapisywania stanów na serwerze, np. :
 stan aplikacji,

stan sesji,

obiekt Cache.
Możliwe jest także zapisanie stanu po stronie klienta, bezpośrednio na komputerze klienta,
np.:
 cookie,

łańcuchy zapytań,

ukryte pola,

stan widoku .
Wszystkie te metody mają ograniczenia skojarzone z czasem życia. W ASP.NET znajduje się
mnóstwo podsystemów ( np. systemy zarządzania rolami i członkostwem), które
przetrzymują stan użytkowników pomiędzy wieloma transakcjami żądanie-odpowiedź.
Systemy te wymagają takiego sposobu zarządzania stanem, który nie musi być ograniczonym
czasem życia obiektów.
Dla przechowywania stanów są potrzebne zaawansowane
mechanizmy, podobne mechanizmom baz danych. W ASP.NET zapisywanie stanów w
magazynach danych w zaawansowanych trybach realizowane jest przez mechanizmy
dostawców. Dostawca jest obiektem, który umożliwia programowy dostęp do
magazynów danych, procesów i innych składników systemu. Dostawcę informacji np. o
sesji, można wyznaczyć za dopomogą następnych trybów:
 InProc,

StateServer,

SQLServer.
Tryb InProc jest domyślnym. W tym trybie generowane sesje przechowywane są w tym
samym procesie, w którym działa proces roboczy ASP.NET (aspnet_wp.exe). Tryb ten jest
najwydajniejszy spośród wszystkich dostępnych, ale w związku z tym, że sesje
przechowywane są w tym samym procesie, za każdym razem, gdy niszczony jest proces
roboczy, usuwane są także wszystkie sesje. Procesy robocze mogą być niszczone z wielu
powodów, np. wskutek zmiany pliku Web.config, Global.asa lub ustawienia IIS, które
44
wymaga usunięcia procesu po upływie określonego czasu. Przykład konfiguracji w pliku
Web.config w trybie InProc pokazany jest w listingu:
<configuration>
<system.web>
<sessionState mode=”InProc”>
</sessionState>
</ system.web>
</configuration>
Tryb StateServer pozwala na przechowywanie stanu sesji poza procesem aspnet_wp.exe.
Przykład konfiguracji w pliku Web.config w trybie StateServer pokazany jest w listingu:
<configuration>
<system.web>
<sessionState mode=”StateServer”
stateConnectionString=”tcpip=127.0.0.1:42424>
</sessionState>
</ system.web>
</configuration>
W tym przypadku sesje będą przechowane w lokalnym serwerze WWW.
Tryb SQLServer jest najbardziej niezawodnym sposobem przechowywania sesji. Przykład
konfiguracji w pliku Web.config w trybie SQLServer pokazany jest w listingu:
<configuration>
<system.web>
<sessionState mode=”SQLServer”
<allowCustomSqlDatabase=”true”
sqlConnectionString=”Data Source=127.0.0.1;
database=MyDatabase;Integrated Security=SSPI”>
</sessionState>
</ system.web>
</configuration>
ASP.NET zawiera wiele systemów, które wymagają zapisywania oraz odzyskania stanów. To
są np. między innymi następne systemy:
 członkostwo,

zarządzanie rolami,

nawigację strony,

personalizację,

monitorowanie stanu oraz zdarzenia sieciowe,

personalizację Web parts,

zabezpieczenie pliku konfiguracyjnego.
Wymienione wyżej systemy nie wykorzystają zapisywanie stanu w trybie ulotnym, natomiast
dla tych celów są niezbędne przechowywania stanów w bazach danych. Domyślnym
dostawcą w ASP.NET jest baza danych aspnetdb w środowisku MSSQLServerExpress. Kiedy
MSSQLServerExpress jest zainstalowany , to ta baza danych tworzy się w sposób automatyczny
przy uruchomieniu w MS VisualStudio narzędzi administracyjne Website/ASP.NET Configuration lub
przy tworzeniu kontrolek na stronach związanych z dostawcami. To są np. kontrolki grupy login.
45
Wykorzystanie MSSQLServerExpress dla realizacji funkcji dostawców nie jest konieczne. Baza
danych aspnetdb może stworzona w dowolnym środowisku SQL Server. W tym celu może być
wykorzystywane narzędzie systemowe aspnet_regsql.exe. Niżej, na rysunkach 1-6 jest są pokazane
przykładowe kroki do tworzenia bazy dostawców w SQL Server.
Rys.1
Rys.2.
46
Rys.3
Rys.4
47
Rys.5
Rys.6
Żeby wprowadzić zmiany w ścieżkach dostępu do bazy dostawców do innego źródła danych
oprócz MSSQLServerExpress trzeba w ręczną zmodyfikować plik konfiguracyjny w sposób
pokazany w listingu:
<connectionStrings>
<remove name="LocalSqlServer" />
<add name="LocalSqlServer" connectionString="Data Source=HP;Initial
Catalog=aspnetdb;User ID=sa;Password=asdf" />
<!--inne connection stringi-->
</connectionStrings>
48
Nie istnieje możliwości dokonać zmian w ścieżkach dostępu do bazy dostawców na poziomie
narzędzie administracyjną Website/ASP.NET Configuration!
Wstawianie użytkowników
W celu dodania użytkowników do usługi członkowstwa należy ich zarejestrować w
magazynie danych aspnetdb . Ten magazyn danych może być utworzony w środowisku MS
SQL Server Express Edition lub MS SQL Server. W pierwszym przypadku plik aspnetdb.mdf
jest automatyczne utworzony w katalogu App_Data witryny.
Wstawianie użytkowników witryny do bazy aspnetdb można dokonać w dwa sposoby:
 przez kontrolkę WWW CreateUserWizard,
 przez kreator Web Site Administrative Tool (WAT).
Korzystanie z kontrolki WWW CreateUserWizard
Kontrolka WWW CreateUserWizard jest przeznaczona dla realizacji usług członkowstwa i
pozwala wstawić dane użytkownika. Przykład użycia kontrolki na stronie jest pokazany w
listingu:
<body>
<form id="form1" runat="server">
<div>
</div>
<asp:CreateUserWizard ID="CreateUserWizard1" runat="server">
<WizardSteps>
<asp:CreateUserWizardStep runat="server" />
<asp:CompleteWizardStep runat="server" />
</WizardSteps>
</asp:CreateUserWizard>
</form>
</body>
Wykorzystanie kontrolki po domyśleniu wymaga silnych haseł długości 7 symboli. Te
wymagania można zmienić dodając do pliku Web.config aplikacji odpowiednią sekcją,
pokazaną w listingu. Te ustalenia zamieniają ustalenia domyślne pliku machine.config:
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="LocalSqlServer"
requiresQuestionAndAnswer="false"
requiresUniqueEmail="true"
passwordFormat="Hashed"
minRequiredPasswordLength="3"
minRequiredNonalphanumericCharacters="0" />
</providers>
</membership>
W tym listingu atrybut minRequiredPasswordLength="3" wymaga 3 litery dla hasła,
natomiast atrybut minRequiredNonalphanumericCharacters="0" nie wymaga specjalnych
symboli w hasłach.
49
Tworzenie strony powitalnej
Do strony powitalnej (Welcome.aspx) tzeba przeniejść konttrolkę LoginSatus. Ta kontrolka
wyświetla status logowania poprzez wyświetlanie odnośnika. W przypadku gdy użytkownik
został pomyślnie zalogowany do systemu, kontrolka wyświetla odnośnik umożliwiający
wykonanie operacji wylogowania z niego. Użytkownicy niezalogowani w systemie posiadają
odnośnik do strony logowania. Warto by zobaczyć, kto zalogował się do witryny, w tym celu
trzeba przeciągnąć kontrolkę LoginView. Kontrolka LoginView ma dwa widoki:
AnonimousTemplate oraz LoggedInTemplate. To, który z tych szablonów będzie
wyświetlany , zależy od faktu, czy użytkownik się zalogował. Szablon AnonimousTemplate
jest przeznaczony dla użytkowników niezalogowanych.
Kod strony Welcome.aspx jest pokazany w następnym listingu:
Page Language="C#" AutoEventWireup="true" CodeFile="Welcome.aspx.cs"
Inherits="Welcome" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<style type="text/css">
#form1
{
height: 309px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
<asp:LoginStatus ID="LoginStatus1" runat="server" />
<asp:LoginView ID="LoginView1" runat="server">
<LoggedInTemplate>
Witaj&nbsp;<br />
<asp:LoginName ID="LoginName2" runat="server" />
<br />
<br />
&nbsp;<asp:HyperLink ID="HyperLink1" runat="server"
NavigateUrl="~/ProfileInfo.aspx">Dodaj
dane do profiu</asp:HyperLink>
</LoggedInTemplate>
<AnonymousTemplate>
Nie jesteś zalogowany. Kliknij lącze Logowanie ....
</AnonymousTemplate>
</asp:LoginView>
<asp:LoginName ID="LoginName1" runat="server" />
</form>
</body>
</html>
50
Tworzenie strony logowania
Do witryny WWW należy dodać następną stronę , o nazwie Login.aspx. Strona musi nosić
dokładnie taką nazwę , ponieważ w przeciwnym wypadku inne kontrolki nie będą mogli jej
wywołać. W listingu są pokazane kody strony Login.aspx z kontrolką Login.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs"
Inherits="Login" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
<asp:Login ID="Login1" runat="server" BackColor="#F7F6F3"
BorderColor="#E6E2D8"
BorderPadding="4" BorderStyle="Solid" BorderWidth="1px" FontNames="Verdana"
Font-Size="0.8em" ForeColor="#333333">
<TextBoxStyle Font-Size="0.8em" />
<LoginButtonStyle BackColor="#FFFBFF" BorderColor="#CCCCCC"
BorderStyle="Solid"
BorderWidth="1px" Font-Names="Verdana" Font-Size="0.8em"
ForeColor="#284775" />
<InstructionTextStyle Font-Italic="True" ForeColor="Black" />
<TitleTextStyle BackColor="#5D7B9D" Font-Bold="True" FontSize="0.9em"
ForeColor="White" />
</asp:Login>
</form>
</body>
</html>
Konfigurowanie aplikacji ASP.NET
Konfigurowanie aplikacji WWW zawiera ustalenie różnego rodzaju parametrów którzy
wyznaczają sposoby kompilacji stron WWW, ich odwzorowania, dostępu do resursów
aplikacji przez użytkowników.
W ASP.NET zostało zrealizowane hierarchiczne
konfigurowanie aplikacji WWW. Wszystkie parametry konfigurowania są wyznaczone w
pliku XML web.config. Ten plik nie może być zmodyfikowany przez przeglądarkę
użytkownika. Wszystkie zmiany w tym pliku mogą być dokonane tylko przez edytor tekstu na
komputerze z ustalonym projektem aplikacji ASP.NET. Te zmiany będą automatyczne
wyznaczone przy uruchomieniu aplikacji oraz nowe parametry konfiguracji będą aktualnymi.
Przy instalacji .NET Framework jest stworzony plik konfiguracji całego komputera który ma
nazwisko
machine.config.
Ten
plik
jest
rozmieszczony
w
katalogu
C:\WINDOWS\Microsoft.Net\Framework\[wersja]\config. W tym pliku są ustalone domyślne
ustawienia dla całego systemu ASP.NET. Wpisy wprowadzone w plikach web.config w
katalogach głównych poszczególnych aplikacji WWW mogą przesłaniać ustawienia z
katalogu głównego. Wpisy z plików web.config znajdujących się w podkatalogach aplikacji
51
mogą przesłaniać wpisy z plików zawartych w katalogach głównych. Plik web.config zawiera
rozdziały wyznaczone odpowiednimi tagami. Lista tych tagów jest pokazana w tabeli.
Tag
Opis
<appSettings>
<authentication>
<athorization>
<browserCaps>
<compilation>
<customErrors>
<globalization>
<httpHandlers>
<httpModules>
<httpRuntime>
<identity>
<macvhineKey>
<pages>
<processModel>
<securitytPolicy>
<sessionState>
<trace>
<webServices>
Dostęp do baz danych za pomocą ADO.NET
ADO.NET to jest nowa technologia dostępu do baz danych w środowisku .NET, zawierająca
odpowiednie klasy, którzy są przeznaczone do realizacji funkcji na bazach danych.
Podstawowymi zmianami, jakie są wprowadzone w obiektach ADO.NET w stosunku do
ADO, są następne:
 Zastosowanie języka XML do wymiany danych. Formaty plików XML są wykorzystane
przy realizacji wszystkich operacji pomiędzy obiektami ADO.NET.
 Sposób współpracy obiektów ADO.NET z bazami danych. Obiekty ADO wymagały
blokowania dostępu do zasobów bazy danych i stałego połączenia aplikacji z bazami
danych. Obiekty ADO.NET korzystają się z odłączonych zbiorów danych (disconnected
data sets) (za pomocą obiektu DataSet), co pozwala uniknąć długotrwałych połączeń i
blokowania danych. W ten sposób aplikacje ADO.NET stają się skalowalne, ponieważ
użytkownicy nie rywalizują o dostęp do baz danych.
 Funkcjonalna specjalizacja obiektów. W ADO był realizowany płaski model obiektowy,
to oznaczy się że ta sama funkcja mogła być zrealizowana przez różne obiekty. Na
przykład, obiekt Connection w ADO jest przeznaczony do połączenia z bazą danych, oraz
ten obiekt można jednocześnie wykorzystać dla realizacji poleceń do baz danych, lub
obiekt RecordSet jest przeznaczony dla przechowywania rezultatów poleceń do bazy
danych, oraz ten obiekt może być wykorzystany do połączenia z bazą danych. W
ADO.NET funkcji wszystkich obiektów są wyspecjalizowane oraz nie przeplatają się z
sobą.
Zmiany wprowadzone w technologii ADO.NET w stosunku do technologii ADO są
pokazane w następnej tabeli.
Tabela
52
Technologia ADO
Przedstawienie danych:
Obiekt Recordset, przypominający
pojedynczą tabelę lub wynik kwerendy
Dostęp do danych:
Sekwencyjny dostęp do wierszy zapisanych
w obiekcie RecordSet.
Relacje pomiędzy wieloma tabelami:
Dane z wielu tabel można połączyć w
jednym obiekcie RecordSet za pomocą
instrukcji SQL JOIN i UNION.
Współdzielenie danych:
Konieczne jest dokonanie konwersji typu
danych na typ akceptowany przez systemodbiorcę, co obniża wydajność aplikacji.
Skalowalność :
Wynikiem walki o dostęp do źródła danych
jest blokowanie dostępu do bazy danych oraz
połączenia z bazą.
Zapory ogniowe:
Zapory mogą blokować wiele rodzajów
zapytań.
Technologia ADO.NET
Obiekt DataSet, który może zawierać wiele
tabel z wielu źródeł danych
Umożliwia całkowicie niesekwencyjny
dostęp do danych zapisanych w obiekcie
DataSet za pomocą hierarchii kolekcji
Do przechodzenia pomiędzy powiązanymi
tabelami służą obiekty DataRelation.
Korzysta się z XML, więc konwersje typów
nie są konieczne.
Nie występuje blokowanie dostępu do bazy
danych ani długotrwałe aktywne połączenia,
więc nie ma walki o dostęp do danych.
Zapory są przezroczyste dla plików XML.
Klasy ADO.NET są rozmieszczone w następnych przestrzeniach nazw:
 System.Data – dostarcza podstawowych funkcji służących do dostępu do danych. Do tej
przestrzeni nazw należą klasy ADO.NET: DataSet, DataTable, DataRow, DataView i
inne. Przestrzeń nazw System.Data zawiera również zbiór SqlDbType, którego elementy
reprezentują wbudowane typy danych SQL Server.
 System.Data.Common - zawiera klasy, których możliwości są wspólne dla wszystkich
dostawców dostępu do danych .NET. Najważniejszą klasą tej przestrzeni nazw jest
abstrakcyjna klasa DataAdapter. Stanowy ona pomost pomiędzy obiektem DataSet a
źródłem danych. Klasa ta jest dziedziczona przez inną klasę abstrakcyjną –
DbDataAdapter, która stanowi podstawę do implementacji klas adaptujących dla
konkretnych dostawców (obecnie są to klasy OleDbAdapter i SqlDataAdapter).
 System.Data.OleDb – najważniejszymi klasami tej przestrzeni są OleDbDataAdapter,
OleDbCommand, OleDbDataReader i OleDbConnection. Te cztery klasy, we współpracy
z klasą DataSet, udostępniają praktycznie wszystkie funkcje konieczne do współpracy ze
źródłami danych opartymi na OLE DB. Do współpracy z SQL Server można zastosować
OLE DB, ale dla lepszej wydajności są polecane stosowanie dostawcy SqlClient.
 System.Data.SqlClient - najważniejszymi klasami tej przestrzeni są SqlDataAdapter,
SqlCommand, SqlDataReader i SqlConnection. Te klasy służą do współptacy z SQL
Server. Podobnie jak odpowiednie klasy przestrzeni nazw System.Data.OleDb
umożliwiają przeprowadzenie dowolnych operacji na bazach danych SQL Server.
 System.Data.SqlTypes – zawiera struktury odpowiadające wbudowanym typom danych
SQL Server.
53
DataSet
Data Table
Row
Column
Constraint
DataRelation
Dostawca ADO.NET
DataAdapter
DataReader
Command
Connection
DataSource
Rys.21
Schemat budowy ADO.NET jest pokazany na rys.21. Głównym magazynem danych w
ASP.NET jest obiekt DataSet. DataSet zawiera pełny zestaw danych, łącznie z
ograniczeniami (constraints), relacjami; może zawierać jednocześnie wiele tabel. DataSet to
jest baza danych w odróżnieniu od obiektu RecordSet w technologii ASP. Obiekty
DataReader (OleDbDataReader oraz SqlDataReader) implementują prostą, jednokierunkową
metodę pobierania danych od bazy danych. Te obiekty przypominają kursory typu
ForwardOnly
w technologii ASP. Funkcjonowanie klas ADO.NET jest docelowo
rozpatrywać w następnych grupach:
1. SqlConnection, SqlCommand, SqlDataReader
2. OleDbConnection, OleDbCommand, OleDbDataReader
3. SqlDataAdapter,OleDbDataAdapter,DataSet,DataTable,DataRelation,DataView
Pierwsza grupa jest przeznaczona dla wykorzystania baz danych w MS SQL Server. Klasy
SqlConnection oraz SqlCommand są podobne obiektom ADO Connection i Command, ale są
przeznaczone dla komunikacji tylko z Microsoft SQL Server. . Klasa SqlDataReader jest
podobna do kursora jednokierunkowego AdForvardOnly w ADO. Ta klasa także jest
przeznaczona dla pracy tylko z
Microsoft SQL Server. Dla połączenia z bazą oraz dla
przesyłania danych klasy pierwszej grupy wykorzystają protokół TDS (Tabular Data Stream).
To jest protokół niskopoziomowy opracowany przez firmę Microsoft dla własnych serwerów
baz danych. Transmisja danych tu jest bardzo wydajna. Wszystkie inne bazy danych
wykorzystają standard OLEDB lub ODBC.
Klasy drugiej grupy realizują funkcji podobne klasom grupy pierwszej dla serwerów innych
producentów, np. Sybase, Oracle, PostgreSql, MySql. Dla połączenia z baza danych te klasy
potrzebują wykorzystania klas odpowiednich providerów OLEDB lub ODBC.
Klasy trzeciej grupy mogą być wykorzystane dla stworzenia prezentacji całej bazy danych w
pamięci RAM. Klasa DataSet może prezentować bazę danych w RAM. DataSet może
54
zawierać klasy DataTable którzy prezentują tabele w bazie danych. Klasy DataTable są
stworzone i uzupełniane danymi za pomocą klasy SqlDataAdapter lub OleDbDataAdapter.
Za dopomoga klasy DataRelation można ustalić różne relacji pomiędzy tabelami. Klasa
DataView pozwoli sortować i filtrować dane w tabelach.
Pierwsze dwie grupy wykorzystają stale połączenie z bazami danych. To znaczy, że obiekty
klas DataReader tych grup mogą otrzymać tylko jeden rekord z bazy danych przy
uruchomieniu metody Read() dopóty dopóki jest aktywne połączenie z bazą danych. Dla
otrzymania innych rekordów trzeba stworzyć pętlę z tych odwołań Read() . Trzecia grupa
pozwoli stworzyć kopię bazy danych bezpośrednio w pamięci RAM oraz odłączyć się od
serwera bazy danych i pracować z baza w trybie autonomicznym. Aplikacja może pracować
w tym przypadku z kopią bazy danych bez obciążenia głównego serwera bazy danych.
Otwarcie połączenia z baza danych
Przykład połączenia z baza danych Microsoft SQL Server jest pokazany w listingu
SqlConnection.aspx.
Listing SqlConnection.aspx.
1. <%@ Page CodeBehind="SqlConnection.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SqlConnection" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. myConnection = New SqlConnection( "server=localhost;database=Pubs;uid=sa;pwd="
)
7. myConnection.Open()
8. %>
9. Connection Opened!
W pierwszych liniach kodu są importowane przestrzeni nazw klas dla połączenia z serwerem
SQL. W następnych liniach kodu został stworzony obiekt klasy SqlConnection. Dla
inicjalizacji obiektu myConnection konstruktor klasy SqlConnection zawiera wiersz
połączenia . W wierszu połaczenia są niezbędnie dani dla połaczenia z serwerem SQL: imię
serwera, imię bazy danych, imię użytkownika(login), hasło. Przy wykorzystaniu klasy
SqlConnection nie jest wyznaczony w wierszu połączenia parametr providera. Klasy w
przestrzeni nazw System.Data.SqlClient nie wykorzystają providery OLEDB, ODBC.
Wszystkie te klasy pracują z protokołem TDS. Wiersz połączenia oprócz tego nie może
zawierać imię źródła danych w postaci DSN (data source name). DSN może być
wykorzystany tylko razem z odpowiednimi klasami przestrzeni nazw System.Data.OleDb.
Przykład otwarcia bazy danych Microsoft Access jest pokazany w listingu
OleDbConnection.aspx.
Listing.
1. <%@ Page CodeBehind="OleDbConnection.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.OleDbConnection" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.OleDb" %>
4. <%
5. Dim myConnection As OleDbConnection
6. myConnection = New OleDbConnection(
"PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA
Source=c:\inetpub\iissamples\sdk\asp\database\authors.mdb" )
7. myConnection.Open()
55
8. %>
9. Connection Opened!
Microsoft Access nie może wykorzystać protokół TDS dlatego w listingu
OleDbConnection.aspx jest importowana przestrzeń System.Data.OleDb. Konstruktor klasy
OleDbConnection zawiera wiersz połączenia do bazy danych authors.mdb. Wiersz połączenia
zawiera parametr Provider. Ten parametr wyznacza imię providera dla dostępu do bazy
danych Access. Dla otwarcia połączenia z bazą danych przez providery OLEDB oraz ODBC
można wykorzystać DSN, np. :
myConnection = New OleDbConnection( "DSN=myDSN" )
Dodawanie danych przy pomocy ADO.NET
Dodawanie danych może być zrealizowane za dopomogą instrukcji SQL INSERT. Dla
uruchomienia instrukcji SQL INSERT trzeba zrealizować następne kroki:
1. Stworzyć obiekt połączenia z bazą danych oraz otworzyć połączenie.
2. Stworzyć obiekt klasy SqlCommand lub OleDbCommand odpowiadający instrukcji
INSERT.
3. Uruchomić instrukcję INSERT.
Przykład dodawania danych do bazy danych MS SQL Server jest pokazany w listingu
SQLINSERTcommand.aspx. Dodawanie realizuje się za dopomogą obiektu klasy
SqlCommand, któremu jest przypisana instrukcja SQL Insert. Instrukcja INSERT zostanie
uruchomiana za dopomogą metody ExecuteNonQuery(). Ta metoda wraca ilość rekordów
które zostali wpisane do bazy danych.
Listing SQLINSERTcommand.aspx.
1. <%@ Page CodeBehind="SQLINSERTcommand.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLINSERTcommand" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myCommand As SqlCommand
7. myConnection = New SqlConnection(
"server=localhost;uid=sa;pwd=;database=myData" )
8. myConnection.Open()
9. myCommand = New SqlCommand( "Insert testTable ( col1 ) Values ( 'Hello' )",
myConnection )
10. myCommand.ExecuteNonQuery()
11. myConnection.Close()
12. %>
13. New Record Inserted!
W listingu OleDbINSERTcommand.aspx jest przykład dodawania danych do bazy danych
ACCESS.
Listing OleDbINSERTcommand.aspx.
1. <%@ Page CodeBehind="OleDbINSERTcommand.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.OleDbINSERTcommand" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.OleDb" %>
4. <%
5. Dim myConnection As OleDbConnection
6. Dim myCommand As OleDbCommand
56
7. myConnection
=
New
OleDbConnection(
"PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA
Source=c:\inetpub\iissamples\sdk\asp\database\authors.mdb" )
8. myConnection.Open()
9. myCommand = New OleDbCommand( "Insert INTO Authors ( Author ) Values (
'Simpson' )", myConnection )
10. myCommand.ExecuteNonQuery()
11. myConnection.Close()
12. %>
13. New Record Inserted!
Modyfikacja danych przy pomocy ADO.NET
Modyfikacja danych może być zrealizowane za dopomogą instrukcji SQL UPDATE. Dla
uruchomienia instrukcji SQL UPDATE trzeba zrealizować następne kroki:
1. Stworzyć obiekt połączenia z bazą danych oraz otworzyć połączenie.
2. Stworzyć obiekt klasy SqlCommand lub OleDbCommand odpowiadający instrukcji
UPDATE.
3. Uruchomić instrukcję UPDATE.
Przykład modyfikacji danych bazy danych MS SQL Server jest pokazany w listingu
SQLUPDATEcommand.aspx. Modyfikacja realizuje się za dopomogą obiektu klasy
SqlCommand, któremu jest przypisana instrukcja SQL UPDATE. Instrukcja Update zostanie
uruchomiana za dopomogą metody ExecuteNonQuery().
Listing SQLUPDATEcommand.aspx.
1. <%@ Page CodeBehind="SQLUPDATEcommand.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLUPDATEcommand" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myCommand As SqlCommand
7. myConnection = New SqlConnection(
"server=localhost;uid=sa;pwd=;database=myData" )
8. myConnection.Open()
9. myCommand = New SqlCommand( "UPDATE Authors SET LastName='Smith'
WHERE LastName='Bennett'", myConnection )
10. myCommand.ExecuteNonQuery()
11. myConnection.Close()
12. %>
13. Record Updated!
Dla modyfikacji baz danych innego typu czym MS SQL Server trzeba wykorzystać klasy z
przestrzeni nazw System.Data.OleDb. Przykład modyfikacji bazy danych Microsoft Access
jest pokazany w listingu OleDbUPDATEcommand.aspx.
Listing OleDbUPDATEcommand.aspx.
1. <%@ Page CodeBehind="OleDbUPDATEcommand.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.OleDbUPDATEcommand" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.OleDb" %>
4. <%
5. Dim myConnection As OleDbConnection
6. Dim myCommand As OleDbCommand
57
7. myConnection = New OleDbConnection
8.
( "PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA
9.
Source=c:\inetpub\iissamples\sdk\asp\database\authors.mdb" )
10. myConnection.Open()
11. myCommand = New OleDbCommand ( "UPDATE Authors SET Author='Bennett'
12.
WHERE Author = 'Simpson'", myConnection )
13. myCommand.ExecuteNonQuery()
14. myConnection.Close
15. %>
16. Record Updated!
Usuwanie danych przy pomocy ADO.NET
Usuwanie danych może być zrealizowane za dopomogą instrukcji SQL DELETE. Dla
uruchomienia instrukcji SQL DELETE trzeba zrealizować następne kroki:
1. Stworzyć obiekt połączenia z bazą danych oraz otworzyć połączenie.
2. Stworzyć obiekt klasy SqlCommand lub OleDbCommand odpowiadający instrukcji
DELETE.
3. Uruchomić instrukcję DELETE.
Przykład usuwania danych bazy danych MS SQL Server jest pokazany w listingu
SQLDELETEcommand.aspx. Usuwanie realizuje się za dopomogą obiektu klasy
SqlCommand, któremu jest przypisana instrukcja SQL DELETE. Instrukcja DELETE
zostanie uruchomiana za dopomogą metody ExecuteNonQuery().
Listing SQLDELETEcommand.aspx.
1. <%@ Page CodeBehind="SQLDELETEcommand.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLDELETEcommand" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myCommand As SqlCommand
7. myConnection = New SqlConnection
( "server=localhost;uid=sa;pwd=;database=myData" )
8. myConnection.Open()
9. myCommand = New SqlCommand ( "DELETE testTable WHERE col1='fred'",
myConnection )
10. myCommand.ExecuteNonQuery()
11. myConnection.Close()
12. %>
13. Record Deleted!
Dla usuwania rekordów baz danych innego typu czym MS SQL Server trzeba wykorzystać
klasy z przestrzeni nazw System.Data.OleDb. Przykład usuwania rekordów z bazy danych
Microsoft Access jest pokazany w listingu OleDbDELETEcommand.aspx.
Listing OleDELETEcommand.aspx.
1. <%@ Page CodeBehind="OleDbDELETEcommand.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.OleDbDELETEcommand" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.OleDb" %>
4. <%
5. Dim myConnection As OleDbConnection
58
6. Dim myCommand As OleDbCommand
7. myConnection = New OleDbConnection(
"PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA
Source=c:\inetpub\iissamples\sdk\asp\database\authors.mdb" )
8. myConnection.Open()
9. myCommand = New OleDbCommand( "DELETE FROM Authors WHERE Author =
'Simpson'", myConnection )
10. myCommand.ExecuteNonQuery()
11. myConnection.Close()
12. %>
13. Record Deleted!
Czytanie danych z bazy danych za pomocą ADO.NET oraz obiektów
klasy DataReader
Dla odczytania danych z bazy danych trzeba wykorzystać instrukcję SQL SELECT.
Realizacja instrukcji SQL SELECT zawiera następne kroki:
1. Stworzyć obiekt połączenia z bazą danych oraz otworzyć połączenie.
2. Stworzyć obiekt klasy SqlCommand lub OleDbCommand odpowiadający instrukcji
SELECT.
3. Uruchomić instrukcję , która tworzy obiekt DataReader.
4. Realizować pętlę dla odczytania zawartości obiektu DataReader.
Obiekt DataReader to jest egzemplarz klasy SqlDataReader lub OleDbDataReader w
zależności od typu bazy danych. Klasa DataReader to jest kursor jednokierunkowy bazy
danych dla czytania naprzód (AddForwardOnly). DataReader może przekazać tylko jeden
rekord bazy danych. Dla otrzymania następnego rekordu trzeba wykorzystać metodę Read()
do póki nie będzie koniec rekordów.
Przykład czytania danych jest pokazany w listingu SQLDataReader.aspx.
Listing SQLDataReader.aspx.
1. <%@ Page CodeBehind="SQLDataReader.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLDataReader" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myCommand As SqlCommand
7. Dim myDataReader As SqlDataReader
8. myConnection = New SqlConnection(
"server=localhost;uid=sa;pwd=secret;pwd=;database=Pubs" )
9. myConnection.Open()
10. myCommand = New SqlCommand( "Select * from Authors", myConnection )
11. myDataReader = myCommand.ExecuteReader()
12. While myDataReader.Read()
13. Response.Write( myDataReader( "au_lname" )& "<br>")
14. 'Response.Write( "<br>" )
15. End While
16. myDataReader.Close()
17. myConnection.Close()
18. %>
59
Wykorzystanie parametrów w instrukcjach SQL
Dla przekazania poleceniom SQL konkretnych danych można wykorzystać kolekcje i klasy
parametrów w instrukcjach SQL. Wartościom parametrów mogą być przypisane dane z
formularzy lub z kodu programu. W listingu SQLParameters.aspx jest pokazany przykład
wykorzystania parametrów z klasą SqlCommand.
Listing SQLParameters.aspx.
1. <%@ Page CodeBehind="SQLParameters.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLParameters" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myCommand As SqlCommand
7. Dim FirstName As String = "Robert"
8. Dim LastName As String = "Johnson"
9. myConnection = New SqlConnection(
"server=localhost;uid=sa;pwd=;database=myData" )
10. myConnection.Open()
11. myCommand = New SQLCommand( "Insert Authors ( FirstName, LastName ) Values (
@FName, @LName )", myConnection )
12. myCommand.Parameters.Add( New SqlParameter( "@FName", SqlDbType.Varchar, 30
))
13. myCommand.Parameters( "@FName" ).Value = FirstName
14. myCommand.Parameters.Add( New SqlParameter( "@LName", SqlDbType.Varchar, 30
))
15. myCommand.Parameters( "@LName" ).Value = LastName
16. myCommand.ExecuteNonQuery()
17. myConnection.Close()
18. %>
19. Record Inserted!
W tym kodzie są stworzone dwa parametry @FName oraz @LName . Te parametry
wykorzystają się w instrukcji SQL INSERT dla rezerwowania konkretnych wartości
zmiennych FirstName oraz LastName.
Wykorzystanie zapamiętanych procedur.
Zapamiętane procedury pozwalają wykorzystać ten samy kod SQL przez różne strony
ASP.NET. Wykorzystanie procedur zwiększa wydajność serwera bazy danych.
Listing SQLStoredProcedure.aspx zawiera przykład wykorzystania procedury do wpisania
nowych rekordów do bazy danych.
Listing SQLStoredProcedure.aspx.
<%@ Page CodeBehind="SQLStoredProcedure.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLStoredProcedure" %>
<%@ Import Namespace="System.Data" %>
<%@ Import NameSpace="System.Data.SqlClient" %>
<%
Dim myConnection As SqlConnection
Dim myCommand As SqlCommand
Dim FirstName As String = "Robert"
Dim LastName As String = "Johnson"
myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=myData" )
60
myConnection.Open()
myCommand = New SqlCommand( "InsertAuthors", myConnection )
myCommand.CommandType = CommandType.StoredProcedure
myCommand.Parameters.Add( New SqlParameter( "@FirstName", SqlDbType.Varchar, 30 ))
myCommand.Parameters( "@FirstName" ).Value = FirstName
myCommand.Parameters.Add( New SqlParameter( "@LastName", SqlDbType.Varchar, 30 ))
myCommand.Parameters( "@LastName" ).Value = LastName
myCommand.ExecuteNonQuery()
myConnection.Close
%>
Record Inserted!
Procedura została stworzona w bazie danych myData w MS SQL Server za dopomogą
następnego kodu:
CREATE PROCEDURE InsertAuthors
(
@FirstName Varchar (50),
@LastName Varchar (50)
)
AS
Insert Authors (FirstName, LastName)
Values (@FirstName, @LastName)
W listingu SQLStoredProcedure.aspx procedura zawiera tylko parametry wejściowe.
Procedury zapamiętane mogą mieć parametry wyjściowe oraz przypisywać tym parametrom
odpowiednie wartości. Przykład procedury z parametrami wyjściowymi jest pokazany w
następnym kodzie:
CREATE PROCEDURE getLastName
(
@FName Varchar (50),
@LName Varchar (50) Output
)
AS
Select @LName=LastName
From Authors
Where FirstName = @FName
If @LName is Null
Return (0)
Else
Return (1)
W listingu SQLInputOutput.aspx jest przykład kodu , który wykorzysta zapamiętaną
procedurę.
Listing SQLInputOutput.aspx .
<%@ Page CodeBehind="SQLInputOutput.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLInputOutput" %>
<%@ Import Namespace="System.Data" %>
<%@ Import NameSpace="System.Data.SqlClient" %>
<%
Dim myConnection As SqlConnection
Dim myCommand As SqlCommand
61
Dim myParam As SqlParameter
myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=myData" )
myConnection.Open()
myCommand = New SqlCommand( "getLastName", myConnection )
myCommand.CommandType = CommandType.StoredProcedure
myParam = myCommand.Parameters.Add( New SqlParameter( "RETURN VALUE",
SqlDbType.INT ))
myParam.Direction = ParameterDirection.ReturnValue
myParam = myCommand.Parameters.Add( New SqlParameter( "@FName",
SqlDbType.Varchar, 50 ))
myParam.Direction = ParameterDirection.Input
myParam.Value = "Robert"
myParam = myCommand.Parameters.Add( New SqlParameter( "@LName",
SqlDbType.Varchar, 50 ))
myParam.Direction = ParameterDirection.Output
myCommand.ExecuteNonQuery()
If myCommand.Parameters( "RETURN VALUE" ).Value Then
Response.Write( "The last name is " & MyCommand.Parameters( "@LName" ).Value )
Else
Response.Write( "No author found!" )
END If
myConnection.Close()
%>
Dostęp do danych przy wykorzystaniu obiektów klasy DataSet
Przy wykorzystaniu obiektów klasy DataReader można otrzymać z bazy danych tylko jeden
rekord w ciągu jednego polecenia. To oznaczy że każda metoda Read() powoduje odczytanie
jednego rekordu z bazy danych. Dla odczytania całej bazy do pamięci RAM można
wykorzystać obiekty klas trzeciej grupy ADO.NET. Główną klasą w tej grupie jest klasa
DataSet. Klasa DataSet pozwoli otrzymać w RAM kopię całej bazy danych, dodać nowe
tabele oraz ustalić związki pomiędzy tabelami. Klasa DataSet jest kontenerem dla klas
DataTable oraz DataRelation. Klasa DataTable odpowiada tabeli w bazie danych. Klasa
DataTable może być stworzona dla już istniejącej tabeli bazy lub dla tabeli nowej. Połączenie
pomiędzy klasami DataTable można definiować przez klasę DateRelation, która odpowiada
relacjom w bazie danych. Klasa DataView pozwoli filtrować oraz sortować rekordy klasy
DataTable. Klasa DataView zawiera metody dla poszukiwania informacji wewnątrz klasy
DataTable. Klasy SQLDataAdapter oraz OleDbDataAdapter mogą być wykorzystane dla
stworzenia klasy DataTable z istniejącej tabeli bazy danych.
W celu uzyskiwania dostępu do baz danych w trym przypadku można wyróżnić pięć etapów:
1. Utworzenie obiektu łączącego z bazą danych.
2. Otwarcie połączenia z baza danych.
3. Wypełnienie obiektu DataSet odpowiednimi danymi.
4. Skonfigurowanie obiektu DataView w celu wyświetlania danych.
5. Powiązanie kontrolki WWW (lub HTML) z obiektem DataView.
62
Przykład wykorzystania tych klas dla odwzorowania wszystkich rekordów tablicy jest
pokazany w listingu SQLDataTable.aspx.
Listing SQLDataTable.aspx.
1. <%@ Page CodeBehind="SQLDataTable.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLDataTable" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myDataAdapter As SqlDataAdapter
7. Dim myDataSet As DataSet
8. Dim myRow As DataRow
9. myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=Pubs" )
10. myDataAdapter = New SqlDataAdapter( "Select * From Authors", myConnection )
11. myDataSet = New DataSet()
12. myDataAdapter.Fill( myDataSet, "Authors" )
13. For each myRow in myDataSet.Tables( "Authors" ).Rows
14.
Response.Write( myRow( "au_lname" )& "<br>" )
15. Next
16. %>
W liniach 2,3 są importowane przestrzeni nazw. W tym kodzie jest wykorzystana przestrzeń
nazw System.Data.SqlClient, która zawiera tylko klasy dla połączenia z bazami danych MS
SQL Server. W liniach 5-8 są deklarowane obiekty klas ADO.NET. W linii 9 został
stworzony obiekt myConnection klasy Connection dla połączenia z bazą danych. W linii 10
jest stworzony obiekt myDataAdapter klasy DataAdapter, który odwoła się do wcześnie
stworzonego obiektu myConnection. W linii 11 jest stworzony obiekt myDataSet klasy
DataSet. W linii 12 metoda Fill() obiektu myDataAdapter tworzy w obiekcie myDataSet
obiekt klasy DataTable , który odpowiada tabeli pt. "Authors" w bazie danych. Jednocześnie
do stworzonej tabeli są pompowane dane z bazy danych.
W listingu OleDbDataTable.aspx jest pokazany podobny kod do połączenia z bazą danych
Access. W tym przypadku jest wykorzystana przestrzeń nazw System.Data.OleDb oraz
odpowiedni provider bazy danych.
Listing OleDbDataTable.aspx.
<%@ Page CodeBehind="OleDbDataTable.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.OleDbDataTable" %>
<%@ Import Namespace="System.Data" %>
<%@ Import NameSpace="System.Data.OleDb" %>
<%
Dim myConnection As OleDbConnection
Dim myDataAdapter As OleDbDataAdapter
Dim myDataSet As DataSet
Dim myRow As DataRow
myConnection = New OleDbConnection( "PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA
Source=c:\inetpub\iissamples\sdk\asp\database\authors.mdb" )
myDataAdapter = New OleDbDataAdapter( "Select * From Authors", myConnection )
myDataSet = New DataSet()
myDataAdapter.Fill( myDataSet, "Authors" )
For each myRow in myDataSet.Tables( "Authors" ).Rows
Response.Write( myRow( "Author" ) & "<br>")
63
Next
%>
Klasa DataTable zawiera właściwość Columns, która prezentuje kolekcję kolumn, oraz
właściwość Rows, która prezentuje kolekcję wierszy. Do kolumn można odwołać się w dwa
sposoby:
1. Przez nazwę kolumny w bazie danych
2. Przez indeks kolumny
W listingu SQLShowTable.aspx jest pokazany przykład kodu, który odwzorowuje zawartość
tabeli Authors bazy danych Pubs bez odwołania do nazw kolumn tabeli.
Listing SQLShowTable.aspx.
1. <%@ Page CodeBehind="SQLShowTable.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLShowTable" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myDataAdapter As SQLDataAdapter
7. Dim myDataSet As DataSet
8. Dim myDataTable As DataTable
9. Dim RowCount As Integer
10. Dim ColCount As Integer
11. Dim i, k As Integer
12. myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=Pubs" )
13. myDataAdapter = New SQLDataAdapter( "Select * From Authors", myConnection )
14. myDataSet = New DataSet()
15. myDataAdapter.Fill( myDataSet, "Authors" )
16. RowCount = myDataSet.Tables( "Authors" ).Rows.Count
17. ColCount = myDataSet.Tables( "Authors" ).Columns.Count
18. Response.Write( "<table border=1>" )
19. For i = 0 To RowCount - 1
20.
Response.Write( "<tr>" )
21.
For k = 0 To ColCount - 1
22.
Response.WRite( "<td>" )
23.
Response.Write( myDataSet.Tables( "Authors" ).Rows( i ).Item( k,
DataRowVersion.Current ).toString() )
24.
Response.Write( "</td>" )
25.
Next
26.
Response.WRite( "</tr>" )
27. Next
28. Response.Write( "</table>" )
29. %>
W linii 23 wartość kolumny k w wierszu i jest odczytana za dopomogą metody Item() . Do tej
metody jest przekazana wartość DataRowVersion.Current która powoduje odczytanie
aktualnej wartości kolumny k.
Egzemplarze klasy DataTable mogą być stworzone w sposób programowy. Klasa DataTable
może być kontenerem informacji tymczasowych, np. dla koszyka artykułów w sklepie
internetowym. Przykład stworzenia obiektów klasy DataTable w sposób programowy jest
pokazany w listingu buildDataTable.aspx.
Listing buildDataTable.aspx.
64
1. <%@ Page CodeBehind="buildDataTable.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.buildDataTable" %>
2. <%@ Import Namespace="System.Data" %>
3. <%
4. Dim myDataTable as DataTable
5. Dim myColumn as DataColumn
6. Dim myRow As DataRow
7. Dim i As Integer
8. Dim myRand As System.Random
9. Dim productID As Integer
10. ' Create a DataTable
11. myDataTable = new DataTable("ShoppingCart")
12. myDataTable.MinimumCapacity = 50
13. myDataTable.CaseSensitive = False
14. ' Add an AutoIncrement (Identity) Column
15. myColumn = myDataTable.Columns.Add("ID",
System.Type.GetType("System.Int32") )
16. myColumn.AutoIncrement = TRUE
17. myColumn.AllowDBNull = false
18. ' Add an Integer Column
19. myColumn = myDataTable.Columns.Add("UserID",
System.Type.GetType("System.Int32") )
20. myColumn.AllowDBNull = false
21. ' Add an Integer Column
22. myColumn = myDataTable.Columns.Add("ProductID",
System.Type.GetType("System.Int32") )
23. myColumn.AllowDBNull = false
24. ' Add a String Column
25. myColumn = myDataTable.Columns.Add(
"ProductName",System.Type.GetType("System.String") )
26. myColumn.AllowDBNull = false
27. ' Add a Decimal Column
28. myColumn =
myDataTable.Columns.Add("ProductPrice",System.Type.GetType("System.Decimal"
))
29. myColumn.AllowDBNull = false
30. ' Add Some Data
31. myRand = New Random
32. For i = 0 To 20
33. productID = myRand.Next( 5 )
34. myRow = myDataTable.NewRow()
35. myRow( "UserID" ) = myRand.Next( 3 )
36. myRow( "ProductID" ) = productID
37. myRow( "ProductName" ) = "Product " & productID.toString()
38. myRow( "ProductPrice" ) = 10.25
39. myDataTable.Rows.Add( myRow )
40. Next
41. ' Display All the Rows
42. For each myRow in myDataTable.Rows
65
43. Response.Write( "<hr>" )
44. For each myColumn in myDataTable.Columns
45. Response.Write( myRow.Item( myColumn ).toString() & " / " )
46. Next
47. Next
48. %>
W linii 11 jest stworzony obiekt "ShoppingCart" klasy DataTable. W linii 12 jest wyznaczona
pojemność tego obiektu w 50 rekordów. W linii 13 jest ustalona wartość CaseSensitive, która
pozwoli nie odróżniać małe oraz duże litery. W następnych liniach do klasy DataTable są
dodane 5 kolumn. Kolumna pierwsza ma typ danych AutoIncrement. Następne 4 kolumny
zawierają informację pro towary w koszyku klienta.
Filtracja i sortowanie danych w klasie DataTable
W ADO.NET obiekt DataSet jest odpowiedzialnym tylko za przechowywanie danych. Za
prezentację danych jest odpowiedzialny obiekt DataView. Filtrować oraz sortować dane
można za dopomogą następnych sposobów:
 Przez metodę Select() klasy DataTable
 Przy wykorzystaniu właściwości RowFilter oraz Sort klasy DataViev.
Obiekt DataRow przedstawia wiersz danych zapisanych w obiekcie DataTable. Każdy obiekt
DataRow zawiera właściwość RowState, która wskazuje stan bieżącego wiersza. Właściwość
ta (DataRow.RowState) może mieć wartości: Detached, Unchanged, Added, Deleted oraz
Modified. Opis tych konstant jest pokazany w tablice.
Tabl. Właściwości atrybutu RowState
Nazwa
Wartość Opis
stałej
liczbowa
Detached
1
Obiekt DataRow może być połączony tylko z jednym obiektem
DataTable lub w ogóle nie być jeszcze połączonym z żadnym
obiektem DataTable. W ostatnim przypadku status tego wiersza
jest odłączony – Detached.
Unchanged
2
Ta wartość świadczy, że ten wiersz został wybrany ze źródła
danych oraz nie został zmodyfikowany.
Added
4
Ten Wiersz został dodany do DataTable. Przy uruchomianiu
metody Update obiekt DataAdapter zrealizuje metodę
InsertCommand.
Deleted
8
Ten wierz został wybrany z bazy danych oraz potem został
usunięty. Przy uruchomianiu metody Update obiekt DataAdapter
zrealizuje metodę DeleteCommand.
Modified
16
Ten wierz został wybrany z bazy danych oraz potem został
zmodyfikowany. Przy uruchomianiu metody Update obiekt
DataAdapter zrealizuje metodę UpdateCommand.
Obiekt DataTable zawiera metodę Select() umożliwiającą filtrowanie i sortowanie danych
zapisanych w danej tabeli. Metoda ta zwraca tablicę wierszy (obiektów DataRow). Wywołuje
się ją w następujący sposób:
NazwaTabeli.Select(wyrażenie filtru, porządek sortowania, DataRowViewState)
Dla każdego parametru, który ma być pominięty, należy wpisać NOTHING.
66
Pierwszym parametrem metody Select() jest zawartość klauzuli WHERE z odpowiedniego
polecenia SQL SELECT do bazy danych. Drugim parametrem może być porządek
sortowania w słowie kluczowym SORT polecenia SQL SELECT. Trzeci parametr metody
Select() ustali się wersję RowState. Obiekt DataTable zawiera trzy wersję każdego z
wierszy: pierwotną (DataRowVersion.Original), aktualną(DataRowVersion.Current) lub
proponowaną (DataRowVersion.Proposed). Wersje te służą do określenia stanu wiersza
(atrybut DataRow.RowState). Wersja pierwotna jest to stan wiersza po dodaniu po raz
pierwszy do obiektu DataTable. To są takie same wartości jak w bazie danych. Wersja
aktualna to wersja po wprowadzeniu zmian. Wersja proponowana może być wykorzystana w
trybie edycji grupowej. W trybie edycji grupowej są wykorzystane metody BeginEdit oraz
EndEdit lub CancelEdit klasy DataRow. Wszystkie zmiany muszą być zrealizowane w
metodzie BeginEdit. Te zmiany są buforowane oraz składają wersję proponowaną.
Zatwierdzenie grupowych zmian realizuje się wywołaniem metody EndEdit oraz rezygnacja
ze wszystkich zmian przez metodę CancelEdit. Ten algorytm modyfikowania danych w
DataSet jest pokazany na rys. niżej.
Modyfikowanie wierszy w DATASET
DataSet
Żrodło danych
Wersja
pierwotna
FillDataSet
Wersja pierwotna
DataRowsversion.
Original
Lub
modyfikowanie
Nowa Wersja
pierwotna
Aktualizacja
Wersja aktualna
DataRowsversion.
Currentl
Tryb edytowania wiersza
Anuluj zmiany
Potwierdż zmiany
Wersja
proponowana
DataRowsversion.
Proposed
Rys. 22
67
W listingu wydruk1001.asp.vb jest pokazany przykład odczytu z obiektu DataSet wszystkich
aktualnych wierszy.
Listing wydruk1001.asp.vb
1. Public Class wydruk1001
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6. Dim objConn As New System.Data.OleDb.OleDbConnection _
7. ("Provider=Microsoft.Jet.OLEDB.4.0;" & _
8. "Data Source=C:\aspnet_bazy_test\banking.mdb")
9. Dim objCmd As New System.Data.OleDb.OleDbDataAdapter _
10. ("select * from tblUsers", objConn)
11. Dim ds As System.Data.DataSet = New DataSet
12. objCmd.Fill(ds, "tblDS")
13. Dim dTable As System.Data.DataTable = ds.Tables("tblDS")
14. Dim AktualneWiersze() As System.Data.DataRow = dTable.Select(Nothing, _
15. Nothing, DataViewRowState.CurrentRows)
16. Dim I, J As Integer
17. Dim strOutput As String
18. For I = 0 To AktualneWiersze.Length - 1
19. For J = 0 To dTable.Columns.Count - 1
20.
strOutput = strOutput & dTable.Columns(J). _
21.
ColumnName & " = " & _
22.
AktualneWiersze(I)(J).ToString & "<br>"
23. Next
24. Next
25. Response.Write(strOutput)
26. End Sub
27. End Class
W kodzie metody Page_Load deklaruje się obiekty OleDbConnection oraz
OleDbDataAdapter (linie 6-10). Następne tworzony jest obiekt DataSet, który za pomocą
metody Fill() jest wypełniany danymi(liniey 11-12). Dalej odczytywana jest jedyna tabela
obiektu DataSet, która zapisuje się do zmiennej dTable (linia 13) , aby ułatwić dostęp do
danych . Metoda Select (linie 14-15) odczytuje wszystkie wiersze obiektu DataTable, które
uległy zmianie (wierszy aktualne), i umieszcza je w tablicy. Do przechodzenia do kolejnych
kolumn służy pętla For (linia 18); do przechodzenia do kolejnych kolumn danego wiersza
służy kolejna pętla For (linia 19). Odczytywane są nazwy pól oraz ich zawartość, które po
konwersji na typ string wyświetlane są za pomocą metody Response.Write (linia 25).
Inny przykład wykorzystania metody Select() jest pokazany w listingu SQLSelectFilter.aspx.
Listing SQLSelectFilter.aspx.
68
1. <%@ Page CodeBehind="SQLSelectFilter.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLSelectFilter" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myDataAdapter As SqlDataAdapter
7. Dim myDataSet As DataSet
8. Dim myDataTable As DataTable
9. Dim myRow As DataRow
10. Dim selectRows() As DataRow
11. myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=Pubs"
)
12. myDataAdapter = New SqlDataAdapter( "Select * From Titles", myConnection )
13. myDataSet = New DataSet()
14. myDataAdapter.Fill( myDataSet, "Titles" )
15. selectRows = myDataSet.Tables( "Titles" ).Select( "type='popular_comp'", "title
DESC", DataViewRowState.CurrentRows )
16. For each myRow in selectRows
17. Response.Write( myRow.Item( "title" )& "<br>" )
18. Next
19. %>
Metoda Select() obiektu klasy DataTable jest wykorzystana w linii 15 kodu. Metoda select
tworzy obiekt klasy DataRow, który zawiera kolekcję odfiltrowanych wierszy. Warunki
filtracji są przekazane w parametrach metody Select().
W listingu SQLDataViewFilter.aspx jest pokazany przykład wykorzystania właściwości
RowFilter oraz Sort klasy DataView.
Listing SQLDataViewFilter.aspx.
1. <%@ Page CodeBehind="SQLDataViewFilter.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLDataViewFilter" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myDataAdapter As SqlDataAdapter
7. Dim myDataSet As DataSet
8. Dim myDataTable As DataTable
9. Dim myDataView As DataView
10. Dim myRow As DataRowView
11. myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=Pubs"
)
12. myDataAdapter = New SqlDataAdapter( "Select * From Titles", myConnection )
13. myDataSet = New DataSet()
14. myDataAdapter.Fill( myDataSet, "Titles" )
15. myDataView = myDataSet.Tables( "Titles" ).DefaultView
16. myDataView.RowFilter = "type='popular_comp'"
17. myDataView.Sort = "title DESC"
18. For each myRow in myDataView
19. Response.Write( myRow( "title" )&"<br>" )
20. Next
69
21. %>
Obiekt myDataView klasy DataView tworzy się w linii 15 za dopomogą wywołania
właściwości DefaultView. W liniach 15 oraz 16 właściwościom obiektu myDataView są
przypisane odpowiednie wartości dla filtracji oraz sortowania.
Wykorzystanie klas DataRelation
W obiekcie DataSet mogą być wiele obiektów DataTable. Wykorzystając obiekty
DataRelation można ustalić relacji pomiędzy kolumnami DataTable. W listingu
SQLDataRelation.aspx jest pokazany przykład stworzenia relacji pomiędzy dwoma tabelami.
W tym kodzie są wyznaczone relacje pomiędzy tabelą główną oraz tabelą podrzędną.
Każdemu rekordu tabeli głównej odpowiadają jeden lub wiele rekordów tabeli podrzędnej.
Listing SQLDataRelation.aspx.
1. <%@ Page CodeBehind="SQLDataRelation.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLDataRelation" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myDataAdapter As SqlDataAdapter
7. Dim myDataSet As DataSet
8. Dim myDataTable As DataTable
9. Dim Publisher As DataRow
10. Dim Title As DataRow
11. myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=Pubs" )
12. myDataSet = New DataSet()
13. myDataAdapter = New SQLDataAdapter( "Select * From Publishers", myConnection )
14. myDataAdapter.Fill( myDataSet, "Publishers" )
15. myDataAdapter.SelectCommand = New SqlCommand( "Select * From Titles",
myConnection )
16. myDataAdapter.Fill( myDataSet, "Titles" )
17. myDataSet.Relations.Add( "PubTitles", myDataSet.Tables( "Publishers" ).Columns(
"pub_id" ), myDataSet.Tables( "Titles" ).Columns( "pub_id" ) )
18. For Each Publisher in myDataSet.Tables( "Publishers" ).Rows
19. Response.Write( "<p>" & Publisher( "pub_name" ) & ":" )
20. For Each Title In Publisher.GetChildRows( "PubTitles" )
21. Response.Write("<li>" & Title( "title" ) )
22. Next
23. Next
24. %>
W liniach 5-10 są zadeklarowane zmienne dla obiektów. W linii 11 jest stworzony obiekt
klasy SqlConnection. W linii 12 jest stworzony obiekt DataSet. W linii 13 jest stworzony
obiekt klasy SqlDataAdapter, który zawiera polecenie select oraz odwołanie do obiektu
SqlConnection. W linii 14 za dopomogą metody Fill() jest stworzony obiekt DataTable w
obiekcie DataSet. W liniach 15,16 jest wykorzystany ten samy obiekt DataAdapter dla
stworzenia nowego obiektu DataTable. W linii 17 są ustalone relacje pomiędzy tabelami w
DataSet. W liniach 18-23 są wywołane wszystkie rekordy tabeli podrzędnej dla każdego
rekordu tabeli głównej. Dla wywołania rekordów tabeli podrzędnej jest wykorzystana metoda
GetChildRows() klasy DataRow. Ta metoda ma jeden parametr – imię tabeli podrzędnej.
70
Struktura klas obiektu DataAdapter
Klasa DataAdapter zawiera kolekcję czterech egzemplarzy instrukcji - klas obiektów
Command: SelectCommand, InsertCommand, UpdateCommand, DeleteCommand. Struktura
kolekcji oraz relacje pomiędzy obiektami są pokazane na rys. Przy wykorzystaniu klasy
SqlDataAdapter instrukcji są klasami SqlCommand. Przy wykorzystaniu OleDbDataAdapter
instrukcji są klasami OleDbCommand. Za dopomogą tych instrukcji klasa DataAdapter może
być wykorzystana do wpisywania, modyfikacji oraz usuwania danych w bazie danych.
DataSet
DataAdapter
SelectCommand
Update
Insert
Delete
DataReader
Command
Command
Command
Command
Connection
DataBase
Rys. 22
W listingu SqlDataAdapterUpdate.aspx jest pokazany przykład modyfikacji danych.
Listing SqlDataAdapterUpdate.aspx.
1. <%@ Page CodeBehind="SQLDataAdapterUpdate.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="Adonetwyklad.SQLDataAdapterUpdate" %>
2. <%@ Import Namespace="System.Data" %>
3. <%@ Import NameSpace="System.Data.SqlClient" %>
4. <%
5. Dim myConnection As SqlConnection
6. Dim myDataAdapter As SqlDataAdapter
7. Dim myBuilder As SqlCommandBuilder
8. Dim myDataSet As DataSet
9. Dim myDataTable As DataTable
10. Dim Author As DataRow
11. ' Create the DataSet and DataAdapter
12. myConnection = New SqlConnection( "server=localhost;uid=sa;pwd=;database=Pubs"
)
13. myDataSet = New DataSet()
14. myDataAdapter = New SqlDataAdapter( "Select * From Authors", myConnection )
15. myDataAdapter.Fill( myDataSet, "Authors" )
16. ' Change value of first row
71
17. myDataSet.Tables( "Authors" ).Rows( 0 ).Item( "au_fname" ) = "Jane"
18. ' Update the Database Table
19. myBuilder = New SqlCommandBuilder( myDataAdapter )
20. myDataAdapter.Update( myDataSet, "Authors" )
21. ' Display the Records
22. For Each Author in myDataSet.Tables( "Authors" ).Rows
23. Response.Write( "<p>" & Author( "au_fname" ) & " " & Author( "au_lname" ) )
24. Next
25. %>
W linii 19 jest stworzony obiekt klasy SqlCommandBuilder. Konstruktoru obiektu
SqlCommandBuilder jest przekazany obiekt DataAdapter. Obiekt SqlCommandBuilder
tworzy automatyczne klasy command dla instrukcji Update, Insert, Delete. To oznaczy, że
niema sensu w sposób jawny tworzyć klasy Command dla tych instrukcji.
Wykorzystanie transakcji w ADO.NET
Transakcja to jest grupa operacji z których wszystkie muszą być wykonane prawidłowo lub
żadna z nich nie zostanie zrealizowana. Działanie transakcji polega na wymuszeniu
wykonania wielu zmian w bazie danych w całości. Oznacza to, że albo zostaną dokonane
wszystkie zmiany, albo nie zostanie wykonana żadna z nich. Jeśli na przykład w witrynie
obsługującej sklep elektroniczny znajduje się procedura, która umożliwia użytkownikowi
dodanie pewnej ilości produktów do koszyka zakupów, to równocześnie inna procedura
powinna odjąć daną ilość produktów ze stanu. Obydwie operacji powinny być zgrupowane
razem- trzeba uniknąć sytuacji, w której towar został umieszczony w koszyku i nie został
zdjęty ze stanu magazynowego lub odwrotnie.
ADO.NET dostarcza trzech metod, które działając wspólnie umożliwiają stosowanie
transakcji. Pierwsza z nich jest metoda BeginTransaction() obiektu Connection. Jest ona
stosowana do utworzenia instancji obiektu Transaction, na przykład:
Dim trans As SqlTransaction = conn.BeginTransaction() .
Kolejne dwie metody należą do obiektu Transaction. Przed ich zastosowaniem należy
zastosować obiekt Command do określenia operacji należących do transakcji. Aby to zrobić,
trzeba przypisać właściwości Transaction obiektu Command instancję stworzonego właśnie
obiektu Transaction:
cmd.Transaction = trans .
Po tym już można wykonać oparte na transakcjach operacje na tabelach bazy danych
zdefiniowanych w obiekcie. Uruchomienie metody ExecuteNonQuery()obiektu Command
spowoduje realizację polecenia SQL w trybie transakcyjnym.
Metoda Commit() obiektu Transaction spowoduje zatwierdzenie wszystkich operacji
wykonywanych przez obiekty Command, jeśli wszystkie zakończyły się sukcesem:
trans.Commit() . Z drugiej strony, jeśli wystąpił wyjątek (co oznacza, że któraś operacja nie
powiodła się), metoda RollBack() obiektu Transaction cofnie wszystkie operacje wykonane
przed wystąpieniem błędu: trans.Rollback() .
Przykład realizacji transakcji jest pokazany w listingu wydruk1204.aspx (strona prezentacji
.aspx) oraz w listingu wydruk1204.aspx.vb (kod klasy pośredniej).
Listing wydruk1204.aspx.
1. <%@
Page
Language="vb"
CodeBehind="wydruk1204.aspx.vb"
AutoEventWireup="false" Inherits="Adonetwyklad.wydruk1204" %>
2. <HTML>
3. <body>
4.
<form runat="server">
72
5.
<asp:label id="Label1" runat="server" >
6.
</asp:label>
7.
</form>
8. </body>
9. </HTML>
Listing wydruk1204.aspx.vb
1. Public Class wydruk1204
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6. 'deklarujemy polaczenie'
7.
Dim Conn As New System.Data.OleDb.OleDbConnection("Provider=" & _
8.
"Microsoft.Jet.OLEDB.4.0;" & _
9.
10.
11.
12.
13.
"Data Source=C:\aspnet_bazy_test\banking.mdb")
Dim objTrans As System.Data.OleDb.OleDbTransaction
Dim objCmd As System.Data.OleDb.OleDbCommand = New _
System.Data.OleDb.OleDbCommand _
("DELETE FROM tblUsers WHERE UserID=72", Conn)
14.
15.
16.
Conn.Open()
objTrans = Conn.BeginTransaction()
objCmd.Transaction = objTrans
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
Try
objCmd.ExecuteNonQuery()
objCmd.CommandText = "INSERT INTO tblUsers " & _
"(FirstName, LastName, Address, City, State, " & _
"Zip, Phone) VALUES " & _
"('Jose', 'Santiago', '34 Lake Drive', " & _
"'Yolktown', 'MA', '02515', '8006579876')"
objCmd.ExecuteNonQuery()
objTrans.Commit()
Label1.Text = "Obie operacje zostaly wykonane poprawnie."
Catch ex As System.Data.OleDb.OleDbException
objTrans.Rollback()
Label1.Text = ex.Message & "<p>" & _
"Zadna operacja nie zostala wykonana."
Finally
objCmd.Connection.Close()
End Try
End Sub
End Class
73
Aktualizacja źródeł danych w DOTNET
Po przeprowadzeniu niezbędnych operacji po modyfikacji danych na obiektach DataSet lub
DataTable
może wyniknąć konieczność w aktualizacji całej bazy danych. W procesie
aktualizacji mogą być konflikty przy modyfikacji oddzielnych rekordów tabel bazy danych.
Konflikty te są powodowane następnymi ewentualnymi przyczynami:
 dwa lub więcej użytkowników chcą jednocześnie modyfikować ten samy wiersz w
tabeli bazy danych,
 użytkownik chce modyfikować wiersz, który został już usunięty przez innego
użytkownika,
 użytkownik chce usunąć wiersz do którego inny użytkownik wprowadził odwołanie
przez klucz obcy w innym rekordzie innej tabeli,
 dwa lub więcej użytkownicy chcą wpisać do tej samej tabeli nowe wierszy z tymi
samymi kluczami pierwotnymi,
 oraz inne, skojarzone z koniecznością konkurencyjnego dostępu do informacyjnych
resursów -bazy danych, tabel, wierszy tabel.
Model obiektowy ADONET zawiera decyzji pozwalające uniknąć wymienione wyżej kolizji.
Wszystkie konflikty w bazach danych trzeba rozwiązywać za dopomogą mechanizmów
blokowania informacyjnych resursów. Blokowania pozwalają chronić integralność i
poprawność danych w trybie równoległego dostępu przez wiele użytkowników. W ADO.NET
są dwa typy blokad: blokowanie pesymistyczne (współbieżność pesymistyczna) oraz
blokowanie optymistyczne(współbieżność optymistyczna). W celu zrozumienia treści tego
blokowania rozpatrzymy istniejące mechanizmy ADO oraz ADONET przeznaczone są do
realizacji uporządkowania dostępu do danych przez wiele użytkowników. Głównymi z
podobnych mechanizmów są kursory.
Kursor jest wskaźnikiem do określonego wiersza w zbiorze wynikowym. Innymi słowy, jest
to narzędzie poruszania się w zbiorze wynikowym i pracy nad określonym wierszem –
wskazywanym przez wskaźnik kursora. Kursor nieco jest podobny tablice danych, ale on nie
może w dowolny sposób sortować dani, bo odwzorowuje tylko dani wyznaczone w zapytaniu
SQL do bazy danych na moment uruchamiania tego zapytania.
W technologii ADO zbiór wynikowy jest skojarzony z obiektem klasy Recordset. Natomiast
w ADONET zbiór wynikowy może być skojarzony z klasami DataReader lub DataTable.
ADO ma rozbudowaną obsługę kursorów. Natomiast możliwości
ADO.NET są dość
ograniczone, co oznacza, że nie ma tam obsługi kursorów po stronie serwera . Wynika to z
bezpołączeniowej architektury ADO.NET oraz jego niezależności od źródła danych. Klasa
DataTable zawiera interfejs pozwalający indeksować zawartość klas DataRow, dlatego
ADONET realizuje możliwość uzyskania dostępu do dowolnego wiersza bez przenoszenia
kursora. Wystarczy zdefiniować rozpoczynający się od zera indeks żądanego wiersza i dane
znajdujące się w nim są dostępne bez konieczności sekwencyjnego dostępu.
W wielu źródłach dokumentacji DOTNET pojęcie „Kursor” jest omijane, ale, te pojęcie
najbardziej odpowiada procesom współpracy aplikacji z bazami danych.
Kursory dzielą się według ich cech: położenia i typu.
Ze względu na położenie są dwie grupy kursorów: kursory strony klienta i kursory strony
serwera.
Kursory strony klienta ( lub kursory lokalne), są używane do poruszania się wewnątrz zbioru
wynikowego położonego
lokalne lub po stronie klienta. Oznacza to, że wszystkie wiersze
wybrane kwerendą i kolejne muszą być przesłane do klienta. Zależnie od liczby wierszy może
to być kosztowne w sensie obciążenia sieci i przestrzeni po stronie klienta. Przestrzeń może
74
oznaczać pamięć RAM lub dyskową. ADO.NET korzysta tylko się z kursorów strony klienta
(poza klasą DataReader).
Kursory strony serwera , są używane do poruszania się wewnątrz zbioru wynikowego zdalnie
lub po stronie serwera. ADO.NET nie obsługuje kursorów po stronie
serwera (wyjątkiem
jest klasa DataReader), natomiast ADO popiera tę możliwość. Powodem może być fakt, że
wiele źródeł danych różni się sposobem implementacji i trudno jest udostępnić kursory strony
serwera w taki sposób, aby nie były widoczne komplikacji wynikające z obsługi różnych
źródeł danych.
Ze względu na typy kursorów można wymienić następne:
 kursory jednokierunkowe, przewiane tylko do przodu,
 kursory statyczne,
 kursory dynamiczne,
 kursory kluczowe.
Kursory jednokierunkowe są najmniej kosztowne, pozwalają przesuwać się wewnątrz zbioru
wynikowego tylko po jednym wiersz na raz. Żeby zrealizować powrót do początku kursora
trzeba go zamknąć i otworzyć ponownie, aby przenieść wskaźnik do pierwszego wiersza.
Domyślnie kursor przewiany tylko do przodu jest dynamiczny. Oznacza, że wiersz
wskazywany przez kursor jest odczytywany ze źródła danych , dzięki czemu zostaną
uwzględnione zmiany dokonane w tym samym wierszu już po wygenerowaniu zbioru
wynikowego. Nie dzieje się tak jednak w przypadku klasy DataRead w ADO.NET. Zmiany
w podległych wierszach otwartego obiektu DataReader nie są widoczne. Ten typ kursora jest
jedynym używanym przez klasę DataReader ADO.NET. W ADO ten typ jest jednym z
czterech typów kursorów używanych przez klasę Recordset.
Kursory statyczne zawierają dani zbioru wynikowego w postaci statycznej, to oznaczy się że
zawartość zbioru wynikowego nie zmieni się po jego otwarciu. Zmiany w zbiorze
wynikowym, wprowadzone po jego otwarciu, nie są od razu widoczne, chociaż zmiany
dokonane przez sam kursor statyczny mogą być odczytywane i odzwierciedlone w zbiorze
wynikowym. W odróżnieniu od kursorów przewijanych tylko do przodu, kursor statyczny
może przesuwać się zarówno do przodu, jak i do tyłu. W ADO.NET ten typ kursorów
realizuje się w obiekcie DataSet. W ADO ten typ jest jednym z czterech typów kursorów
używanych przez klasę Recordset.
Kursory dynamiczne są bardzo dobre do obsługi współbieżności, ponieważ
wykrywają
wszystkie zmiany w zbiorze wynikowym dokonane po otwarciu kursora. Kursor dynamiczny
może przesuwać się do przodu i do tyłu. Jest to najbardziej wymagający z opisanych typów
kursorów i jego użycie może okazać się kosztowne, tam, gdzie można go zastąpić innym
kursorem. W ADO.NET ten typ kursorów nie jest wykorzystany. W ADO ten typ jest
jednym z czterech typów kursorów używanych przez klasę Recordset.
Kursory kluczowe łączą funkcje unikatowe kursora statycznego i dynamicznego. Wykrywany
są zmiany
wartości w kolumnach danych. Dane wstawione przez kursor zostaną
dołączone do zbioru wynikowego, ale dane wstawione przez inne kursory będą
widoczne dopiero po zamknięciu i ponownym otwarciu kursora. W ADO.NET ten typ
kursorów nie jest wykorzystany. W ADO ten typ jest jednym z czterech typów kursorów
używanych przez klasę Recordset.
Przystępując do uaktualnienia bazy danych przez kursory trzeba ustalić tryb blokowania bazy
danych. W klasycznym ADO to jest właściwość LockType obiektu Recordset. Blokowanie
jest konieczne, gdyż w tej samej chwili z bazy danych może korzystać większa ilość osób. Są
cztery sposoby blokowania danych które mogą być zrealizowane w klasycznym ADO:
1. Tylko dla odczytu (właściwość Recordset – adLockReadOnly) – rekordy są dostępne
wyłącznie do odczytu i nie można ich modyfikować. Nie można także dodawać
nowych rekordów. Jest to domyślny sposób blokowania.
75
2. Pesymistyczny (właściwość Recordset – adLockPessimistic) – rekordy są blokowane
w momencie rozpoczynania edycji w kursorze. Ma to na celu uniknięcie sytuacji, w
której wartości pól rekordu zmieniają się w trakcie jego edycji(czyli w czasie
pomiędzy rozpoczęciem wpisywania wartości, a wywołaniem metody Update).
3. Optymistyczny
(właściwość Recordset – adLockOptimistic) – rekordy są
blokowane wyłącznie w czasie wykonywania metody Update.
4. Optymistyczny grupowy (właściwość Recordset – adLockBatchOptimistic) –
rekordy nie są blokowane aż do momentu wykonywania aktualizacji grupowej. Ten
sposób blokowania powinien być używany w przypadku korzystania z kursorów
przechowywanych po stronie klienta i odłączonych zbiorów danych.
Model obiektowy ADO.NET nie zawiera możliwości bezpośredniego tworzenia
wymienionych czterech typów kursorów oraz wyznaczenia tych czterech sposobów
blokowania. Ale blokowanie istnieje i w technologii DOTNET. Przystępując do
aktualizowania źródła danych w celu wprowadzenia zmian z obiektów ADO.NET, DataSet i
DataTable, trzeba znać aktualny stan źródła danych. Jest to ważne, ponieważ dane znajdujące
się w obiekcie DataSet lub DataTable mogą być nieaktualnymi, czyli od momentu pobrania
ich ze źródła danych mogły zostać zmienione przez innego użytkownika. To stanowi
poważny problem w przypadku architektury bezpołączeniowej ADO.NET, ponieważ nie ma
standardowego sposobu blokowania wierszy źródła danych na czas edycji obiektu DataSet. W
klasycznym ADO można w zależności od źródła danych określić różne typy i poziomy
blokowania, ponieważ ADO ma architekturę połączeniową. ADO.NET natomiast korzysta się
funkcji blokowania optymistycznego po domyśleniu, oraz mogą być zrealizowane
blokowania resursów informatycznych bazy danych przez blokowanie pesymistyczne.
Blokowanie pesymistyczne.
Blokowanie pesymistyczne (lub współbieżność pesymistyczna) jest typem blokowania
obejmującego część tabeli w źródle danych podczas żądania danych. Ten sposób blokowania
zapobiega wprowadzeniu zmian w źródle danych przez innych klientów w czasie trwania
blokady. Blokada jest zazwyczaj zakładana podczas pobierania danych i zdejmowana po
zakończeniu połączenia ze źródłem danych lub po zwolnieniu pobranych danych.
Blokowanie pesymistyczne może być dobrym wyjściem w środowiskach o dużym natężeniu
danych, co oznacza częste zmiany danych dokonywane przez dużą liczbę klientów . Jeśli
jednak jeden klient blokuje dane dłużej, spowoduje to wystąpienie problemów u innych
klientów, próbujących uzyskać dostęp i zaktualizować te same dane.
W ADO.NET blokowanie pesymistyczne można osiągnąć za pomocą transakcji, zwłaszcza
przy ustaleniu właściwości IsolationLevel. W listingu LockDataTransaction.aspx.vb jest
pokazany przykład wykorzystania transakcji dla realizacji blokowania pesymistycznego.
Listing LockDataTransaction.aspx.vb
1. Partial Class LockDataTransaction
2. Inherits System.Web.UI.Page
3. Const str_connection As String =
"server=localhost;database=Northwind;uid=sa;pwd="
4. Const Str_Sql_User_Select As String = "select * from customers"
5. Dim cnnLocked, cnnUnlocked As System.Data.SqlClient.SqlConnection
6. Dim dadLocked, dadUnlocked As System.Data.SqlClient.SqlDataAdapter
7. Dim traNorthwind As System.Data.SqlClient.SqlTransaction
8. Dim cmdselectUsers As System.Data.SqlClient.SqlCommand
9. Dim dstLocked As New System.Data.DataSet("Locked DataSet")
10.
Dim dstUnLocked As New System.Data.DataSet("UnLocked DataSet")
11.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
12.
'Tworzymy instancje połączenia
76
13.
Me.cnnLocked = New
System.Data.SqlClient.SqlConnection(str_connection)
14.
Me.cnnLocked.Open()
15.
Me.cnnUnlocked = New
System.Data.SqlClient.SqlConnection(str_connection)
16.
Me.cnnUnlocked.Open()
17.
'Rozpoczynamy transakcje
18.
Me.traNorthwind =
Me.cnnLocked.BeginTransaction(System.Data.IsolationLevel.Serializable
)
19.
'Skonfigurujemy polecenie
20.
Me.cmdselectUsers = New
System.Data.SqlClient.SqlCommand(Str_Sql_User_Select, Me.cnnLocked,
Me.traNorthwind)
21.
'tworzymy instancje adapterow
22.
Me.dadLocked = New
System.Data.SqlClient.SqlDataAdapter(Str_Sql_User_Select,
Me.cnnLocked)
23.
Me.dadLocked.SelectCommand = Me.cmdselectUsers
24.
Me.dadUnlocked = New
System.Data.SqlClient.SqlDataAdapter(Str_Sql_User_Select,
Me.cnnUnlocked)
25.
'tworzymy instancje konstruktorow polecen
26.
Dim cmbUser1 As New
System.Data.SqlClient.SqlCommandBuilder(Me.dadLocked)
27.
Dim cmbUser2 As New
System.Data.SqlClient.SqlCommandBuilder(Me.dadUnlocked)
28.
'Wypelniamy zbiory danych
29.
Me.dadLocked.Fill(Me.dstLocked, "Customers")
30.
Me.dadUnlocked.Fill(Me.dstUnLocked, "Customers")
31.
'Aktualizujemy istniejący wiersz w blokowanym obiekcie DataSet
32.
Me.dstLocked.Tables("Customers").Rows(1)("city") =
"Koszalin"
33.
'Aktualizujemy istniejący wiersz w odblokowanym obiekcie
DataSet
34.
dstUnLocked.Tables("Customers").Rows(2)("City") = "Slupsk"
35.
Try
36.
Me.dadLocked.Update(Me.dstLocked, "customers")
37.
'Aktualizujemy odblokowane żrodlo danych
38.
Me.dadUnlocked.Update(Me.dstUnLocked, "customers")
39.
'Wykonujemy transakcje
40.
Me.traNorthwind.Commit()
41.
Me.Label1.Text = "Wszystko OK!"
42.
Catch objE As System.Data.SqlClient.SqlException
43.
'Cofamy transakcję
44.
Me.traNorthwind.Rollback()
45.
Me.Label1.Text = objE.Message
46.
Finally
47.
'Zamykamy
48.
Me.cnnLocked.Close()
49.
Me.cnnUnlocked.Close()
50.
End Try
51.
End Sub
52.
End Class
Strona LockDataTransaction.aspx zawiera kontrolkę Label dla wyświetlania komunikatu.
Kod w listingu LockDataTransaction.aspx.vb deklaruje i wykorzystuje dwa obiekty
SqlDataAdapter, dwa obiekty SqlConnection, dwa obiekty SqlCommandBuilder, jeden obiekt
SqlCommand i jeden obiekt SqlTransaction. Kod strony realizuje połączenie do tego samego
źródła danych w bazie danych „Northwind” oraz tabeli „Customers”, dwóch obiektów
77
DataSet: dstLocked oraz dstUnLocked. Jeden z tych obiektów - dstLocked - realizuje zmiany
w bazie danych przez mechanizm transakcji, drugi, dstUnLocked - próbuje zrealizować
aktualizację bez transakcji, przez zwykłą metodę Update obiektu DataAdapter. Transakcja
traNorthwind została deklarowana w linii 7 oraz ustalona z właściwością
IsolationLevel.Serializable w linii 18.
Realizacja transakcji w środowisku ADO.NET zawiera następną kolejność czynności:
1. Otworzenie połączenia z bazą danych za dopomogą metody Open() obiektu
Connection (linia 14).
2. Uruchomienie metody BeginnTransaction() obiektu Connection(Linia 18). Ta metoda
tworzy obiekt transakcji. Parametrem wejściowym konstruktora transakcji może być
poziom izolacji (linia 18).Wszystkie polecenia do baz danych przez obiekt transakcji
będą zawierali konieczność fiksacji (Commit) lub odwołania (Rollback).Wszystkie
zmiany w bazie danych bez wykorzystania transakcji są wprowadzone natychmiast.
3. Do właściwości Transaction obiektu klasy Command musi być dodany stworzony w
kroku 2 obiekt Transaction oraz do właściwości Connection - obiekt Connection (linia
20). Parametrami wejściowymi konstruktora obiektu Command mogą być polecenie
SQL, obiekt connection oraz obiekt Transaction. Ten samy element transakcji może
być dodany do wszystkich obiektów klasy Command którzy będą zrealizowane w tej
samej transakcji.
4. Stworzenie obiektu klasy CommandBuilder z wyznaczeniem obiektu klasy
DataAdapter w konstruktorze klasy CommandBuilder(). Obiekt klasy
CommandBuilder tworzy obiekty klas InsertCommand, DeleteCommand,
UpdateCommand (linia 26).
5. Uruchomienie transakcji (linia 36), zatwierdzenie (linia 40) lub odwołanie (linia 44).
Poziom izolacji Serializable jest sposobem blokowania „Pesymistyczny’. Ten sposób
blokuje wszystkie rekordy w bazie danych zgodnie z poleceniem SQL : „select * from
customers”. Żadne polecenie do bazy danych nie będzie zrealizowane dopóki transakcja nie
będzie zatwierdzona (Commit) lub skasowana(Rollback). Blokada „Serializable” założona na
źródło danych spowoduje wystąpienie wyjątku przekroczenia czasu operacji po wywołaniu
operacji Update() na obiekcie adaptera danych dadUnlocked. To oznaczy, że wprowadzenie
zmian z drugiego obiektu DataSet, dstUnLocked nie zostanie zrealizowane w czasie trwania
transakcji, oraz będzie sformowany komunikat: „Timeout expired. The timeout period elapsed
prior to completion of the operation or the server is not responding. The statement has been
terminated.” Po tym komunikacie zostanie odwołana i sama transakcja, oraz żadna zmiana nie
zostanie zrealizowana w bazie „Northwind”.
Blokada ta nie zapobiegnie odczytowi danych przez innych klientów, nie dopuści jedynie do
ich aktualizowania i usuwania.
Blokowanie pesymistyczne jest sprzeczne z głównej filozofią ADO.NET, która polega w
następnym:
 Podłączenie do bazy danych musi trwać tylko na period wywołania źródłowych
danych oraz wprowadzenia zmian danych aktualizowanych. Wykorzystanie
poziomów izolacji transakcji powoduje ograniczenie dostępu do danych na period
obrabiania tych danych przez transakcje.
 Wykorzystanie transakcji blokuje połączenie z bazą i nie pozwoli wykorzystać pulę
obiektów Connection.
Realizacja blokowania pesymistycznego może przebiegać w sposób inny, bez wykorzystania
transakcji. Wszystkie polecenia w bazach danych mogą być zrealizowane na poziomie baz
danych , wykorzystując obiekt klasy. Dla uporządkowania dostępu do danych w bazie danych
można stworzyć kursor bazy danych. Typy możliwych kursorów zostali już rozpatrzone.
78
Różne SZBD zawierają różne typy tych kursorów. Stworzyć kursor można na poziomie
aplikacji przez instrukcję SQL, CREATE CURSOR w obiekcie DbCommand. Po stworzeniu
kursora można wykorzystać instrukcji sterowania kursorem.
Blokowanie optymistyczne
Blokowanie optymistyczne (lub współbieżność optymistyczna) nie realizuje nakładanie
jakikolwiek blokad bezpośrednio do źródła danych. Ten sposób blokowania pozwoli
wyeliminować sytuację, kiedy te same dane mogą być zmodyfikowane w źródle danych przez
dowolnego innego klienta z odpowiednimi uprawnieniami. Rozważmy przykład. Pobieramy
dane ze źródła danych do obiektu DataSet. W czasie, gdy ich używamy, te same dane są
zmodyfikowane w źródle danych przez innego użytkownika. Następne zwracamy do źródła
zmodyfikowane dane i skutecznie nadpisujemy zmiany dokonane w czasie pomiędzy
pobraniem a aktualizacją. W tej kolizji wygrywa ostatni klient.
Blokowanie optymistyczne może wykorzystać możliwości chronienia różnych wersji danych
w obiektach DataTable, zwłaszcza wrsji : Original, Current, oraz Proposed. Przy zmianie w
źródle danych dowolnej wartości kolumny przez innego użytkownika, wartość tego pola w
obiekcie DataTable wersji Original będzie już innej. To oznaczy, że warunkiem możliwości
dokonania zmian w źródle danych musi być porównanie wartości pól wersji Original z
wartością tego pola w samym źródle. To oznaczy, że dla realizacji polecenia SQL, UPDATE
trzeba nie tylko wykorzystać klucz pierwotny w klauzuli WHERE, natomiast dodać wszystkie
pierwotne wartości pól rekordu , który będzie aktualizowany. Kiedy to porównanie nie jest
skuteczne, to w ADO.NET wynika zdarzenie DBConcurrencyException.
Przykład demonstracji blokowania optymistycznego jest pokazany w listingu
Concurrency.sapx.vb.
Listing Concurrency.sapx.vb.
1. Partial Class Concurrency
2. Inherits System.Web.UI.Page
3. Const str_connection As String =
"server=localhost;database=Northwind;uid=sa;pwd="
4. Const str_sql_user_select As String = "select * from customers"
5. Dim cnnUser1, cnnUser2 As System.Data.SqlClient.SqlConnection
6. Dim dadUser1, dadUser2 As System.Data.SqlClient.SqlDataAdapter
7. Dim dstUser1 As New DataSet("User1_DataSet")
8. Dim dstUser2 As New DataSet("User2_Dataset")
9. Dim cmbUser1 As System.Data.SqlClient.SqlCommandBuilder
10.
Dim cmbUser2 As System.Data.SqlClient.SqlCommandBuilder
11.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
12.
'Tworzymy instancje polaczen i otwieramy je
13.
Me.cnnUser1 = New
System.Data.SqlClient.SqlConnection(str_connection)
14.
Me.cnnUser1.Open()
15.
Me.cnnUser2 = New
System.Data.SqlClient.SqlConnection(str_connection)
16.
Me.cnnUser2.Open()
17.
'Tworzymy instancje i inicjalizujemy adaptery danych
18.
dadUser1 = New
System.Data.SqlClient.SqlDataAdapter(str_sql_user_select, cnnUser1)
19.
dadUser2 = New
System.Data.SqlClient.SqlDataAdapter(str_sql_user_select, cnnUser2)
20.
cmbUser1 = New
System.Data.SqlClient.SqlCommandBuilder(dadUser1)
21.
cmbUser2 = New
System.Data.SqlClient.SqlCommandBuilder(dadUser2)
79
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
dadUser2.UpdateCommand = Me.cmbUser2.GetUpdateCommand
'Wypelniamy zbiory danych
dadUser1.Fill(dstUser1, "customers")
dadUser2.Fill(dstUser2, "customers")
'Aktualizujemy wiersz w pierwszym obiekcie DataSet
dstUser1.Tables("Customers").Rows(0)("City") = "Warszawa"
'Aktualizujemy żrodło danych z pierwszego obiektu DataSet
Me.dadUser1.Update(dstUser1, "Customers")
'Aktualizujemy wiersz w drugiem obiekcie DataSet
dstUser2.Tables("Customers").Rows(0)("Address") =
"Pilsudskiego 2"
32.
Try
33.
'Aktualizujemy żrodło danych z drugiego obiektu DataSet
34.
Me.dadUser2.Update(dstUser2, "Customers")
35.
Catch objE As DBConcurrencyException
36.
Me.Label1.Text = "komunikat zdarzenia
DBConcurrencyException: " & objE.Message
37.
Me.Label2.Text = "Update Command: " &
dadUser2.UpdateCommand.CommandText
38.
Finally
39.
cnnUser1.Close()
40.
cnnUser2.Close()
41.
End Try
42.
End Sub
43. End Class
W listingu są definiowane (linie 18-19) dwa obiekty adaptera danych, które wypelniają dwa
obiekty DataSet (linie 24-25). W obiektach DataSet wprowadzone zmiany w tym samym
wierszu różnych kolumn tabeli Customers. Pierwsza zmiana przez adapter dadUser1 zostanie
wprowadzona do żrodła danych (linia 29). Druga zmiana powoduje zdarzenie
DBConcurrencyException.
Wadą tego sposobu blokowania jest niemożliwość sprawdzić pola danych typu BLOB, którzy
mogą zawierać setki megabajtów. W tym przypadku mechanizm generowania poleceń SQL
w obiekcie SqlCommandBuilder ignoruje te pole.
Innym podejściem dla realizacji blokowania optymistycznego jest stworzenie w tabelach
dodatkowych kolumn znaczników czasowych Timestamp. Wartość tej kolumny zmienia się
przy każdej realizacji dowolnej operacji DML. W tym przypadku klauzula WHERE może
zawierać dwa parametry : identyfikator rekordu oraz wartość kolumny TimeStamp z wersii
ORIGINAL.
Użycie XML
Standard XML realizuje dostęp aplikacjom do danych na podstawie deskryptorów opisu tych
danych w pliku. Przy tym nie są ważne kwestii fizycznego rozlokowania tych danych w
pliku. Format XML może być wykorzystany na różnych platformach komputerowych, oraz
jest przezroczystym dla zapór ogniowych i innych systemów bezpieczeństwa komputerów.
Przestrzeń nazw System.Xml zawiera większą część klas .NET dla pracy z językiem XML.
Głównymi z tych klas są następne:
XmlAttribute
XmlNamedNodeMap
XmlTextWriter
XmlDocument
XmlNodeList
XmlValidatingReader
XmlElement
XmlNodeReader
XPathDocument
XmlNode
XmlTextReader
XslTransform
80
Czytanie danych XML
Dla odczytania zawartości dokumentów XML może być wykorzystana klasa XmlTextReader.
Ta klasa jest podobna klasie OleDbDataReader lub SqlDataReader w sensie realizacji
jednokierunkowego odczytu informacji od początku do końca dokumentu. Przykład strony dla
odczytania zawartości pliku XML jest pokazany w listingu XMLReader.aspx(VB).
Listing XMLReader.aspx(VB). Witryna XML.(Chris Payne)
1.
2.
3.
4.
5.
Imports System.Xml
Namespace XML
Partial Class XMLReader
Inherits System.Web.UI.Page
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6.
Dim reader As XmlTextReader
7.
Try
8.
reader = New XmlTextReader(Server.MapPath("books.xml"))
9.
While reader.Read()
10.
Response.Write("<b>" & reader.Name & "</b> " & _
11.
reader.Value & "<br>")
12.
End While
13.
Catch ex As Exception
14.
Response.Write("Blad dostepu do pliku XML!")
15.
Finally
16.
reader.Close()
End Try
17.
End Sub
18.
End Class
19.
End Namespace
Uruchomienie tego pliku wyświetla wszystkie znaczniki zapisane w pliku books.xml – nie
tylko znaczniki otwierające, lecz także zamykające. Ta informacja nie zawsze jest potrzebna.
Aby wyeliminować zbyteczną informację trzeba wykorzystać właściwość NodeType klasy
XmlTextReader. Przykład wykorzystania tej wlaściwości jest pokazany w listingu
XMLReader2.aspx(VB).Listing XMLReader2.aspx(VB). Witryna XML. (Chris Payne)
1. Imports System.Xml
2. Namespace XML
3. Partial Class XMLReader2
4.
Inherits System.Web.UI.Page
5.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
6.
Dim reader As XMLTextReader
7.
Dim i As Integer
8.
'Dim curPrice As Decimal
9.
Try
10.
reader = New XMLTextReader(Server.MapPath("books.xml"))
11.
While reader.Read()
12.
Select Case reader.NodeType
13.
Case XMLNodeType.Element
14.
If reader.HasAttributes Then
15.
For i = 0 To reader.AttributeCount - 1
16.
Response.Write(reader.GetAttribute(i) _
17.
& " ")
18.
Next
19.
Response.Write("<br>")
20.
End If
21.
Case XMLNodeType.Text
22.
Response.Write(reader.Value & "<br>")
23.
End Select
81
24.
25.
26.
27.
28.
29.
30.
31.
32.
End While
Catch ex As Exception
Response.Write("Błąd dostępu do pliku XML!")
Finally
reader.close()
End Try
End Sub
End Class
End Namespace
W wierszu 12 została wstawiona instrukcja select, która przed wyświetleniem jakichkolwiek
informacji sprawdza typ aktualnie przetwarzanego węzła. Pierwszy blok CASE , w wierszu
13, jest wykonywany gdy właściwość NodeType ma wartość Element. Jeśli węzeł ma
jakiekolwiek atrybuty(reader.HasAttributes=true), są one kolejno pobierane oraz
wyświetlane, a do określenia ich wartości używana jest metoda GetAttribute(). Jeśli węzeł jest
zwyczajnym fragmentem tekstu oraz nie zawiera atrybutów, jest on wyświetlany w wierszu
22.
Zapis danych XML
Zapisywanie danych do pliku XML może być zrealizowane za dopomogą obiektów klasy
XmlTextReader. Przykład wykorzystania tej klasy jest pokazany w listingu
XMLWriter.aspx(VB).Listing XMLWriter.aspx(VB). Witryna XML. (Chris Payne).
1. Imports System.Xml
2. Namespace XML
3. Partial Class XMLWriter
4.
Inherits System.Web.UI.Page
5.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6.
Dim writer As XMLTextWriter
7.
Try
8.
writer = New XMLTextWriter(Server.MapPath _
9.
("books2.xml"), Nothing)
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
writer.WriteStartDocument()
writer.Formatting = Formatting.Indented
writer.Indentation = 3
writer.WriteStartElement("bookstore")
writer.WriteStartElement("book")
writer.WriteAttributeString("genre", "history")
writer.WriteAttributeString("style", "hardcover")
writer.WriteElementString("title", "Połtawa 1709")
writer.WriteStartElement("author")
writer.WriteElementString("first-name", _
"Władisław A.")
writer.WriteElementString("last-name", _
"Serczyk")
writer.WriteEndElement()
writer.WriteElementString("price", _
"19.99")
writer.WriteEndElement()
writer.WriteEndElement()
writer.Flush()
Catch ex As Exception
Response.Write("Błąd dostępu do pliku XML!")
Finally
writer.Close()
Response.Write("Zakończono generację pliku")
End Try
End Sub
82
36.
37.
End Class
End Namespace
Obiekt Writer jest deklarowany w linii oraz stworzony w linii 8-9. Pierwszym argumentem
przekazywanym w wywołaniu konstruktora jest nazwa tworzonego pliku XML, a drugim sposób kodowania (domyśłnie kod UTF-8). W wierszu 10 zapisywany jest znacznik
deklaracjiXML : <?xml version="1.0"?> . W wierszach 11. i 12. definiowany jest
sposób , w jaki ma być zapisywany generowany kod XML. Wiersz 11 definiuje stosowanie
wcięcia, a wiersz 12 – że pojedyncze wcięcie musi mieć 3 znaki odstępu. Generacja danych
rozpoczyna się w wierszu 13. W tym wierszu jest zgenerowany znacznik otwierający
<bookstore>. Natomiast w wierszu 28 jest wygenerowany znacznik zamykający
</bookstore>.
Walidacja plików XML
Walidacja danych XML jest możliwa na podstawie schematu dokumentu(XDR-XML Data
Reduced) lub definicją typu dokumentu (DTD – Document Type Definition). Dla walidacji
jest wykorzystywana klasa XmlValidatingReader. W walidacji uczestniczy klasa
XmlTextReader, która realizuje szybki dostęp do danych dokumentu XML w kierunku od
początku do końca bez keszowania tych danych. Jednak klasa XmlTextReader nie zawiera
metod do walidacji, dlatego XmlValidatingReader działa wspólnie z klasą XmlTextReader.
Przykład wykorzystania tych klas dla walidacji dokumentów XML jest pokazany w listingu
XmlVaidate.aspx(VB).
1. Imports System.Xml
2. Imports System.Xml.Schema
3. Namespace XML
4. Partial Class XMLValidate
5.
Inherits System.Web.UI.Page
6.
Private reader As XMLTextReader
7.
Private validator As XMLValidatingReader
8.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
9.
System.EventArgs) Handles MyBase.Load
10.
Try
11.
reader = New XmlTextReader(Server.MapPath("books2.xml"))
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
validator = New XMLValidatingReader(reader)
validator.ValidationType = ValidationType.XDR
AddHandler validator.ValidationEventHandler, New _
ValidationEventHandler(AddressOf ShowError)
While validator.Read()
End While
Catch ex As Exception
Response.Write("Błąd dostępu do pliku XML!")
Finally
reader.close()
End Try
End Sub
Sub ShowError(ByVal obj As Object, ByVal e As
ValidationEventArgs)
25.
Response.Write("<font color=""red"">" & e.Message & _
26.
"<br>")
27.
If (reader.LineNumber > 0) Then
28.
Response.Write("Line: " & reader.LineNumber & _
29.
" Position: " & reader.LinePosition & _
30.
"</font><p>")
31.
End If
32.
End Sub
33.
End Class
83
34. End Namespace
Związki pomiędzy obiektami klas DataSet oraz Xml
XML w środowisku DOTNET jest ściśle związany z ADO.Net. Dane wewnątrz obiektów
DataSet są przechowywane w formacie XML. Oznacza to, że zawartość obiektów DataSet
jest przechowywana w pamięci komputera jako dane XML, a nie reprezentowana w inny
model danych. A zatem, na tych danych można wykorzystać klasy służące do obsługi XML.
Klasa XmlDataDocument jest dla XML tym, czym DataSet jest dla ADO.NET. Obiekty obu
tych klas są do siebie podobny i każdy z nich można skonwertować na drugi. Dzięki temu
klasy te stanowią pomost pomiędzy ADO.NET oraz XML. Gdy obiekt XmlDataDocument
zostanie ładowany środowisko DOTNET automatycznie tworzy obiekt DataSet, który można
pobrać za pośrednictwem właściwości o tej samej nazwie. Kolumny tego obiektu oraz ich
typy określane są na podstawie schematu XML. Jeśli żaden schemat nie zostanie podany, to
środowisko DOTNET samodzielnie określi strukturę danych.
Dzięki temu można modyfikować dane w dowolny sposób. Na przykład można otworzyć plik
XML przy wykorzystaniu klas XML, a następnie przenieść je do obiektu DataSet i powiązać
elementami kontrolek WWW. Można także pobrać informacje przechowywane w bazie
danych przy użyciu DataSet i zapisać w formacie XML. Modyfikacje wprowadzane w
obiektach XmlDataDocument mogą, lecz nie muszą powodować zmian w obiektach DataSet.
Jeśli nowe dane odpowiadają polom obiektu DataSet, to zostanie do niego dodany nowy
wiersz. Wzajemne związki pomiędzy obiektami klas XmlDataDocument oraz DataSet są
pokazane na rys.
Plik XML
XmlTextreader
XmlDataDocument
Walidacja XML,
Transformacja
kodu XML,itp.
Baza Danych
DataAdapter
DataSet
Nnnnz
Wiązanie danych
Rys. Wzajemne związki pomiędzy obiektami klas XmlDataDocument oraz DataSet
Przykład wykorzystania klas DataSet oraz XmlDataDocument jest pokazany w listingu
XmlDataSet.aspx(VB).
1.Imports System.Xml
2.Namespace XML
3.
Partial Class XMLDataset
4.
Inherits System.Web.UI.Page
5.
Private i, j As Integer
6.
Private strOutput As String = ""
84
7.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
8.
Dim xmldoc As New XMLDataDocument()
9.
Try
10.
xmldoc.DataSet.ReadXml(Server.MapPath("books3.xml"))
'select data view and bind to server control
11.
DataGrid1.DataSource = xmldoc.DataSet
12.
DataGrid1.DataMember = xmldoc.DataSet.Tables(0). _
13.
TableName
14.
DataGrid2.DataSource = xmldoc.DataSet
15.
DataGrid2.DataMember = xmldoc.DataSet.Tables(1). _
16.
TableName
17.
DataGrid1.DataBind()
18.
DataGrid2.DataBind()
19.
For i = 0 To xmldoc.DataSet.Tables.Count - 1
20.
strOutput += "TableName = """ & _
21.
xmldoc.DataSet.Tables(i).TableName & """<br>"
22.
strOutput += "&nbsp;&nbsp;" & "Columns count " & _
23.
"= " & xmldoc.DataSet.Tables(i).Columns.Count. _
24.
ToString() & "<br>"
25.
For j = 0 To xmldoc.DataSet.Tables(i).Columns.Count - 1
26.
strOutput += "&nbsp;&nbsp;&nbsp;&nbsp;" & _
27.
"ColumnName = """ & xmldoc.DataSet. _
28.
Tables(i).Columns(j).ColumnName & """, " & _
29.
"type = " & xmldoc.DataSet.Tables(i). _
30
Columns(j).DataType.ToString() & "<br>"
31.
Next
32.
Next
33.
strOutput += "<p>"
34.
Catch ex As Exception
35.
strOutput = "Error accessing XML file"
36.
End Try
37.
output.Text = strOutput
38.
End Sub
39. End Class
40. End Namespace
W linii 8 został stworzony obiekt klasy XmlDataDocument. Ten obiekt nie wczytuje sam
bezpośrednio dane. Dane są pobierane za pośrednictwem metody ReadXml właściwości
DataSet (wiersz10). Ta metoda tworzy relacyjną reprezentację pobieranych informacji. W
liniach 19-24 oraz 25-31 są wykorzystana włożona pętla for do pobierania nazw tabeli i
kolumn oraz typy danych reprezentowane przez każdą kolumnę. W liniach 11-18 jest
pokazany sposób na wiązanie danych z kontrolkami DataGrid.
Wykorzystanie obiektowego modelu dokumentu XML dla czytania
danych
Obiektowy model dokumentu XML (DOM) jest specyfikacją zawierającą klasy dla
odwzorowania oraz dla manipulowania zawartością dokumentu w trybie nie strumieniowym.
W tym trybie dokument XML jest skojarzony z klasami i kolekcjami klas zgodnie ze
strukturą tego dokumentu. Z całym dokumentem jest skojarzona klasa XmlDocument. Ta
klasa jest przeznaczona dla otwierania dokumentu. Podstawowe możliwości funkcjonalne
opisane przez XML DOM udostępnia klasa XmlNode która jest klasą potomną
XmlDocument. Klasa XmlNode reprezentuje pojedynczy element należący do drzewa
dokumentu XML i może zostać wykorzystana do przejścia do węzłów podrzędnych oraz do
węzła nadrzędnego, jak również do edycji i usuwania informacji. Z klasą XmlNode są
skojarzone kolekcje obiektów klas XmlElement czy XmlAttribute. Przy wykorzystaniu klas
85
DOM Programista może mieć dostęp do wszystkich węzłów, elementów oraz atrybutów
dokumentu, a nie tylko do oddzielnych znaczników w trybie strumieniowym przez klasy
XmlTextReader lub XmlTextWriter. Model obiektowy DOM wykorzysta dla odczytania oraz
zapisywania informacji do pliku XML klasy XMLTextReader oraz XmlTextWriter w sposób
przezroczysty dla programisty. Współdziałanie tych klas jest pokazane na rys.
W3C XML DOM
XML Document
XmlTextRead
er
XML Node
Dane XML
XmlTextWrit
er
Rys. Współdziałanie klas służących do obsługi danych XML.
Przykład wykorzystania modelu DOM jest pokazany w listingu XMLDOMRead.aspx(VB)
1. Imports System.Xml
2. Namespace XML
3.
Partial Class XMLDOMRead
4.
Inherits System.Web.UI.Page
5.
Private i As Integer
6.
Private strOutput As String = ""
7.
8.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
9.
Dim xmldoc As New XmlDocument()
10.
11.
Try
12.
xmldoc.Load(Server.MapPath("books.xml"))
13.
ShowTree(xmldoc.DocumentElement)
14.
15.
Catch ex As Exception
16.
strOutput = "Błąd dostępu do pliku XML!"
17.
End Try
18.
19.
output.Text = strOutput
20.
End Sub
21.
Sub ShowTree(ByVal node As XmlNode)
22.
Dim attrnode As XmlNode
23.
Dim map As XmlNamedNodeMap
24.
25.
If Not (node.HasChildNodes) Then
26.
strOutput += "&nbsp;&nbsp;<b>" & _
27.
node.Name & "</b> &lt;" & _
28.
node.Value & "&gt;<br>" & vbCrLf
29.
Else
30.
strOutput += "<b>" & node.Name & "</b>"
31.
If node.NodeType = XmlNodeType.Element Then
32.
map = node.Attributes
86
33.
34.
For Each attrnode In map
strOutput += " <b>" & _
attrnode.Name & "</b> &lt;" & _
attrnode.Value & "&gt; " & vbCrLf
Next
End If
strOutput += "<br>"
End If
35.
36.
37.
38.
39.
40.
41.
42.
If node.HasChildNodes Then
43.
node = node.FirstChild
44.
While Not IsNothing(node)
45.
ShowTree(node)
46.
node = node.NextSibling
47.
End While
48.
End If
49.
End Sub
50. End Class
51. End Namespace
W wierszu 9 tworzony jest obiekt klasy XmlDocument. W wierszu 12 są do niego
wczytywane informacje z pliku XML. Procedura ShowTree (linie 21-49) analizuje wszystkie
węzły tego pliku i wyświetla informacje o nich. Procedura ShowTree wykorzysta rekursję w
linii 45.
Modyfikacja danych w obiektowym modelu dokumentów XML
Obiekt XmlDocument udostępnia sporo metod służących do tworzenia i modyfikacji
dokumentów XML. Przykład tworzenia nowego elementu w dokumencie XML jest
pokazany w listingu XMLDOMWrite.aspx(VB).
1. Imports System.Xml
2. Namespace XML
3.
Partial Class XMLDOMWrite
4.
Inherits System.Web.UI.Page
5.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
6.
Dim xmldoc As New XMLDocument()
7.
Dim strOutput As String = ""
8.
Try
9.
xmldoc.Load(Server.MapPath("books3.xml"))
10.
Dim eleBook As XmlElement = _
11.
xmldoc.CreateElement("book")
12.
Dim attStyle As XmlAttribute = _
13.
xmldoc.CreateAttribute _
14.
("style")
15.
16.
eleBook.SetAttributeNode(attStyle)
17.
eleBook.SetAttribute("style", "hardback")
18.
19.
Dim root As XmlElement = xmldoc.Item("bookstore")
20.
root.AppendChild(eleBook)
21.
22.
xmldoc.Save(Server.MapPath("books3.xml"))
23.
24.
Catch ex As Exception
25.
strOutput = "Błąd dostępu do danych XML!"
26.
End Try
27.
28.
output.Text = "Poprawnie dopisano dane do plik XML"
29.
End Sub
87
30. End Class
31. End Namespace
W linii 6 został zadeklarowany obiekt klasy XMLDocument. W linii 9 stworzona zostala
instancja tego obiektu na bazie pliku „ boks3.xml”. W liniach 10-11 jest stworzony nowy
element ("book"). W liniach 12-14 do zostal stworzony nowy atrybut ("style"). W linii 16
ten atrybut został dodany do elementu „book”. W linii 17 ustalona wartość ("hardback")
nowego atrybutu. W liniach 19-20 nowy element został dodany do dokumentu. W linii 22
dokument został zapisany do pliku.
Wiązanie danych
Wiązanie danych umożliwia kontrolowanie danych. Prawie każdy typ danych może być
powiązany z dowolnym obiektem sterującym lub atrybutem tego obiektu. Daje to całkowitą
kontrolę nad sposobem przekazywania danych z magazynu danych do strony i z powrotem.
Dane można wiązać ze stroną ASP.NET w następne sposoby:
1. Użycie atrybutu DataSource danego obiektu sterującego
2. Za pomocą wyrażenia wiążącego dane.
Pierwszy sposób jest wykorzystany w kontrolkach zawierające właściwość DataSource do
której może być przypisany obiekt danych DataReader lub obiekt DataView klasy DataSet.
Klasy tych kontrolek zawierają metodę DataBind().
Sposób drugi jest wykorzystany w kontrolkach nie zawierających właściwość DataSource.
Składnia wyrażenia wiążącego dane w tym przypadku jest następująca:
<%# atrybut lub kolekcja %>
Chociaż składnia ta bardzo
przypomina bloki wykonywania kodu tradycyjnej technologii
ASP, tak nie jest. Bloki wykonywania kodu są zawsze przetwarzane w trakcie wykonywania
kodu strony.
Wyrażenia wiążące dane są przetwarzane tylko za pomocą metody DataBind(). Jeśli metoda
będzie wywoływana na poziomie kodu strony, to wtedy przetworzone będą wszystkie
wyrażenia wiążące dane, zawarte w kodzie. Metodę tę wywołuję się w procedurze zdarzenia
Page_Load(). Metodę DataBind() można wywoływać również dla każdego z obiektów
sterujących z osobna, co daje większą kontrolę nad sposobami korzystania z danych w
konkretnej aplikacji.
Konieczność wiązania danych do obiektów sterujących wynika w przypadkach
komplikowanych operacji manipulowania danymi otrzymanymi z bazy danych w celu ich
prezentacji, np. dla odwzorowania listy rozwianej w kontrolkach, realizacji skrolingu,
sortowania oraz edytowania danych.
Przykład wiązania danych pomiędzy kontrolką Label oraz kontrolką ListBox jest pokazany w
listingu Wydruk0906.asp.
Listing Wydruk0906.asp.
1. <script Language="VB" runat="server">
2. sub Index_Changed(obj as Object, e as EventArgs)
3.
DataBind()
4. end sub
5. </script>
6. <html><body>
7. <form runat="server">
8.
<asp:ListBox runat="server" id="lbKolory"
9.
width="150"
10.
AutoPostBack=true
11.
rows="3"
88
12.
SelectionMode="Single"
13.
OnSelectedIndexChanged="Index_Changed" >
14.
<asp:Listitem value=" 1">Red</asp:Listitem>
15.
<asp:Listitem value=" 2">Blue</asp:Listitem>
16.
<asp:Listitem value=" 3">Green</asp:Listitem>
17.
<asp:Listitem value=" 4">Yellow</asp:Listitem>
18.
</asp:Listbox><p>
19.
<asp:Label id="lblKomunikat" runat="server"
20.
Text='<%# lbKolory.selectedItem.Text %>' />
21. </form>
22. </body></html>
W liniach 8-18 została zdefiniowana kontrolka ListBox. W liniach 14-17 są wyznaczone
wartości pól ListItem tej kontrolki. Z kontrolką ListBox jest skojarzone zdarzenie
Index_Changed które zdefiniowane jest w liniach 2-4. Realizacja tego zdarzenia powoduje
uruchomienie metody DataBind () dla całej strony . Kontrolka Label jest zdefiniowana w
liniach 19-20. Kontrolka Label może wykorzystać tylko drugi sposób wiązania danych. Z
polem Text tej kontrolki w linii 20 została powiązana wartość wybranego pola w kontrolce
ListBox. Wyrażenie w linii 20 może być bezpośrednio wpisane w kodzie strony aspx lub
wyznaczone w designerze VSNET. W ostatnim przypadku trzeba odtworzyć okno
właściwości kontrolki Label, klikniejć pole DataBinding, wpisać wyrażenie do okna Custom
binding expression.
Bardzo ważnymi kontrolkami służącymi do przetwarzania struktur listowych (list controls) są
obiekty sterujące: Repeater, DataList oraz DataGrid. Obiekty te automatycznie przechodzą
kolejno do poszczególnych elementów kolekcji danych. Te obiekty działają jako kontener dla
innych obiektów sterujących, które faktycznie wyświetlają dane ( np. kontrolka Label).
Wymienione wyżej obiekty mają duże możliwości w stworzeniu interfejsu użytkownika.
Kontrolka która prezentuje dani musi mieć właściwość DataSource. Do tej właściwości może
być przypisany identyfikator obiektu klasy DataReader lub obiektu DataView. Ostateczne
wiązanie danych pomiędzy kontrolką oraz źródłem danych odbędzie się po wywołaniu
metody DataBind().Na rys. 23 są pokazane główne składnie procesu wiązania danych
kontrolek serwerowych zawierających właściwość DataSource oraz metodę DataBind().
89
Kontrolka serwerowa
DataSource
Żrodlo
danych
DataBin
d ()
Cache
Controls
Bufor strony
Rys. 23
Wiązanie danych z kontrolką Repeater oraz obiektem DataReader
Kontrolka Repeater jest jedną z podstawowych kontrolek służących do wyświetlania danych.
W przeciwieństwa do kontrolek DataList i DataGrid nie zawiera żadnych stylów ani
właściwości formatowania. Jeśli jest potrzeba formatować dane w tej kontrolce, to można
użyć do tego celu znaczniki HTML.
W ogóle kontrolka Repeater jest kontenerem, służącym do przetwarzania kolejnych pozycji
listy danych. Sposób wyświetlania danych nie jest wcześniej zdefiniowany – układ
wyświetlania danych może być określony za pomocą szablonów (template controls). Dzięki
temu interfejs można dostosować do konkretnej aplikacji. Szablony – to są obiekty sterujące,
umożliwiające stosowanie języka HTML oraz innych obiektów do sterowania wyświetlaniem
danych w kontrolkach. W technologii .NET są trzy kontrolki sterujące które pozwalają
wykorzystać szablony: Repeater, DataList oraz DataGrid.
W przypadku kontrolki Repeater, można stosować następujące szablony:
 ItemTemplate – Szablon ten jest konieczny dla kontrolki Repeater. W wyniku działania
tego szablonu każdy wiersz danych wyświetlany jest oddzielnie.
 AlternatingItemTemplate – szablon taki sam jak ItemTemplate, ale przetwarzany jest
oddzielie dla każdego wiersza danych. Umożliwia to określanie ustawień stylu dla
poszczególnych wierszy.
 HeaderTemplate oraz FooterTemplate – szablony te przetwarzają kod HTML
bezpośrednio przed i po wyświetleniu wszystkich wierszy danych.
90

SeparatorTemplate – szablony te przedstawiają elementy znajdujące się pomiędzy
poszczególnymi wierszami danych.
Zdarzenia kontrolki Repeater:
Zdarzenie
Opis
ItemCommand
Zdarzenie zgłaszane, gdy kliknięty zostanie przycisk znajdujący w
kontrolce Repeater
ItemCreated
Zdarzenie zgłaszane, gdy tworzony jest element kontrolki Repeater
ItemdataBound
Zdarzenie zgłaszane, po przeprowadzeniu wiązania danych elementu
kontrolki Repeater, ale przed wyświetleniem go na stronie
Przykład wiązania danych z elementem Repeater jest pokazany w listingu
SqlRepeaterSimple.aspx.
Listing SqlRepeaterSimple.aspx.
1. <%@ Page CodeBehind="SqlRepeaterSimple.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlRepeaterSimple" %>
2. <HTML>
3. <HEAD>
4. <title>Titles</title>
5. </HEAD>
6. <body>
7. <form Runat="Server">
8. <asp:Repeater id="myRepeater" Runat="Server">
9.
<ItemTemplate>
10.
<hr>
11.
<%# Container.DataItem( "title" ) %>
12.
<blockquote>
13.
<%# Container.DataItem( "notes" ) %>
14.
</blockquote>
15.
</ItemTemplate>
16. </asp:Repeater>
17. </form>
18. </body>
19. </HTML>
W tym kodzie w liniach 8-16 jest deklarowana kontrolka Repeater. Wewnątrz deskryptora
<asp:Repeater > jest wyznaczony deskryptor <ItemTemplate>. Ten deskryptor wyznacza
sposób prezentacji każdego rekordu w elemencie Repeater. Wyrażenia dla wiązania danych są
wyznaczone w liniach 11 oraz 13. Wyrażenie Container.DataItem( "title" ) odwoła się
bezpośrednio do obiektu Repeater oraz do elementu "title" tablicy.
Klasa pośrednia dla tego pliku jest pokazana w listingu SqlRepeaterSimple.aspx.vb. Wiązanie
danych jest wyznaczone w liniach 14, 15. Kod w linii 14 realizuje powiązanie obiektu
SqlDataReader z kontrolką Repeater. Kod w linii 15 ląduje dane od DataReader do kontrolki
Repeater.
Listing SqlRepeaterSimple.aspx.vb.
1.
2.
3.
4.
Imports System.Data.SqlClient
Imports System.Data
Public Class SqlRepeaterSimple
Inherits System.Web.UI.Page
5. #Region " Web Form Designer Generated Code "
91
6. #End Region
7. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
8. 'Put user code to initialize the page here
9. Dim myConnection As SqlConnection
10. Dim myCommand As SqlCommand
11. myConnection = New
SqlConnection("Server=Localhost;uid=sa;pwd=;Database=Pubs")
12. myCommand = New SqlCommand("Select title, notes From Titles", myConnection)
13. myConnection.Open()
14. myRepeater.DataSource = myCommand.ExecuteReader()
15. myRepeater.DataBind()
16. myConnection.Close()
17. End Sub
18. End Class
Przy odwzorowaniu danych może być sytuacja, kiedy pola w bazie danych mogą zawierać
wartości NULL. W tych przypadkach można wykorzystać wiązanie z funkcją, która formuje
na stronie rezultaty, kiedy ta wartość nie jet NULL. Przykład wiązania z funkcją jest
pokazany w listingach SqlRepeaterFixNulls.aspx oraz SqlRepeaterFixNulls.aspx.vb.
W listingu SqlRepeaterFixNulls.aspx w sekcji <body> …</body> zostało zdefiniowane
wiązanie z funkcją fixNulls. Funkcja fixNulls został zdefiniowana w listingu
SqlRepeaterFixNulls.aspx.
Listing SqlRepeaterFixNulls.aspx.
1. <%@ Page CodeBehind="SqlRepeaterFixNulls.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlRepeaterFixNulls" %>
2. <HTML>
3. <HEAD>
4.
<title>Titles</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:Repeater id="myRepeater" Runat="Server">
9.
<HeaderTemplate>
10.
<table border="1" cellspacing="0" cellpadding="5">
11.
</HeaderTemplate>
12.
<ItemTemplate>
13.
<tr>
14.
<td><%# fixNulls( Container.DataItem
15.
( "title" ).ToString() ) %></td>
16.
<td><%# fixNulls( Container.DataItem
17.
( "notes" ).ToString() ) %></td>
18.
</tr>
19.
</ItemTemplate>
20.
<AlternatingItemTemplate>
21.
<tr bgcolor="lightyellow">
22.
<td><%# fixNulls( Container.DataItem
23.
( "title" ).ToString() ) %></td>
24.
<td><%# fixNulls( Container.DataItem
92
25.
( "notes" ).ToString() ) %></td>
26.
</tr>
27.
</AlternatingItemTemplate>
28.
<FooterTemplate>
29.
</table>
30.
</FooterTemplate>
31.
</asp:Repeater>
32.
</form>
33. </body>
34. </HTML>
Listing SqlRepeaterFixNulls.aspx.vb
1.
2.
3.
4.
5.
6.
Imports System.Data.SqlClient
Imports System.Data
Public Class SqlRepeaterFixNulls
Inherits System.Web.UI.Page
Function fixNulls(ByVal theString As String) As String
If theString = "" Then
fixNulls = "[NULL]"
7. Else
fixNulls = theString
8. End If
9. End Function
10.
#Region
11.
#End Region
12.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
13.
Dim myConnection As SqlConnection
14.
Dim myCommand As SqlCommand
15.
myConnection = New
SqlConnection("Server=Localhost;uid=sa;pwd=;Database=Pubs")
16.
myCommand = New SqlCommand("Select title, notes From Titles",
myConnection)
17.
myConnection.Open()
18.
myRepeater.DataSource = myCommand.ExecuteReader()
19.
myRepeater.DataBind()
20.
myConnection.Close()
21.
End Sub
22. End Class
Wykorzystanie kontrolki REPEATER oraz obiektu DataReader dla
formowania listy wypunktowanej.
Niżej jest pokazany przykład kodu dla wypunktowania rekordów na stronie internetowej.
Każdy rekord bazy danych wypunktuje się w oddzielnym wierszu tabeli.
Listng SqlrepeaterIndex.aspx
1. <%@ Page CodeBehind="SqlRepeaterIndex.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlRepeaterIndex" %>
2. <HTML>
3. <HEAD>
4.
<title>Titles</title>
5. </HEAD>
6. <body>
93
7.
<form Runat="Server">
8.
<asp:Repeater id="myRepeater" Runat="Server">
9.
<HeaderTemplate>
10.
<table border="1" cellspacing="0" cellpadding="5">
11.
</HeaderTemplate>
12.
<ItemTemplate>
13.
<tr>
14.
<td><%# Container.ItemIndex + 1 %></td>
15.
<td><%# Container.DataItem( "title" ) %></td>
16.
<td><%# Container.DataItem( "notes" ) %></td>
17.
</tr>
18.
</ItemTemplate>
19.
<AlternatingItemTemplate>
20.
<tr bgcolor="yellow">
21.
<td><%# Container.ItemIndex + 1 %></td>
22.
<td><%# Container.DataItem( "title" ) %></td>
23.
<td><%# Container.DataItem( "notes" ) %></td>
24.
</tr>
25.
</AlternatingItemTemplate>
26.
<FooterTemplate>
27.
</table>
28.
</FooterTemplate>
29.
</asp:Repeater>
30.
</form>
31.
</body>
32. </HTML>
Dla odwzorowania numeru pozycji rekordu jest wykorzystana właściwość ItemIndex
każdego elementu klasy Repeater Item.
W listingu SqlrepeaterIndex.aspx.vb jest pokazany kod klasy pośredniej. W liniach 12,13
zrealizowane zostało powiązanie kontrolki Repeater z obiektem DataReader.
Listing SqlrepeaterIndex.aspx.vb
1. Public Class SqlRepeaterIndex
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6.
Dim myConnection As System.Data.SqlClient.SqlConnection
7.
Dim myCommand As System.Data.SqlClient.SqlCommand
8.
9.
10.
myConnection = New
System.Data.SqlClient.SqlConnection _
("Server=Localhost;uid=sa;pwd=;Database=Pubs")
myCommand = New System.Data.SqlClient.SqlCommand("Select title,
notes From Titles", myConnection)
11.
myConnection.Open()
12.
myRepeater.DataSource = myCommand.ExecuteReader()
13.
myRepeater.DataBind()
14.
myConnection.Close()
15.
End Sub
16. End Class
94
Wiązanie danych z kontrolką DropDownList
Kontrolka DropDownList zawiera właściwość DataSource która musi być ustalona do
wywołania metody DataBind(). Przykład wykorzystania tej kontrolki jest pokazany w listingu
SqlDropDownList.aspx.
Listing SqlDropDownList.aspx.
1. <%@ Page CodeBehind="SqlDropDownList.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDropDownList" %>
2. <HTML>
3. <HEAD>
4.
<title>Categories</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
Please select a category:
9.
<br>
10.
<asp:dropdownlist id="category" Runat="Server">
11.
</asp:dropdownlist>
12.
<asp:button id="Button1" Runat="server" Text="Select!">
13.
</asp:button>
14.
<p>Current Category:
15.
<asp:label id="currentCat" Runat="Server">
16.
</asp:label>
17.
</form>
18.
</P>
19. </body>
20. </HTML>
Kod klasy programowej jest pokazany w listingu SqlDropDownList.aspx.vb.
Listing SqlDropDownList.aspx.vb.
1. Public Class SqlDropDownList
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6. 'Put user code to initialize the page here
7. Dim myConnection As System.Data.SqlClient.SqlConnection
8. Dim myCommand As System.Data.SqlClient.SqlCommand
9. If Not IsPostBack Then
10.
myConnection = New System.Data.SqlClient.SqlConnection ("Server
Localhost; uid=sa; pwd=; Database
= Northwind" )
11.
myCommand = New System.Data.SqlClient.SqlCommand ("Select
CategoryName From Categories", myConnection)
12.
myConnection.Open()
13.
category.DataSource = myCommand.ExecuteReader()
14.
category.DataTextField = "CategoryName"
15.
category.DataBind()
16.
myConnection.Close()
17. End If
18. End Sub
95
19. Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Handles Button1.Click
20.
currentCat.Text = category.SelectedItem.Text
21. End Sub
22. End Class
Wykorzystanie kontrolki DataList
Kontrolka DataList jest bardzo podobna do kontrolki Repeater, ale umożliwia użytkownikom
pracę interaktywną i modyfikowanie danych. Kontrolkę trzeba stosować razem z szablonami
do wyświetlania poszczególnych pozycji w postaci listy, tak samo jak w przypadku Repeater.
W przypadku DataList można stosować dodatkowo dwa szablony:
 SelectedItemTemplate – szablon ten zawiera dodatkowe elementy, które pojawią wtedy,
kiedy użytkownik wybierze któraś z pozycji wyświetlanych przez DataList. Typowym
zastosowaniem może być zmiana stylu wyświetlania wiersza po jego wybraniu .
 EditItemTemplate – Szablon ten określa sposób wyświetlania wybranej pozycji w trybie
edytowania.
Kontrolka DataList zawiera następne zdarzenia:
 OnItemCommand – to zdarzenie może być inicjowane przez inny element sterujący
(kontrolkę), który jest zawarty w szablonie ItemTemplate kontrolki DataList. Element
sterujący musi być rozmieszczony w szablonie ItemTemplate oraz również musi mieć
możliwość generacji zdarzeń. Zdarzenie powodowane tym elementem nie może być
zdarzeniem
OnEditCommand,
OnUpdateCommand,
OnDeleteComand,
OnCancelCommand.
 OnEditCommand - to zdarzenie może być inicjowane przez inny element sterujący
(kontrolkę), który jest zawarty w kontrolce DataList. Właściwość CommandName tego
elementu musi mieć wartość Edit.
 OnUpdateCommand - to zdarzenie może być inicjowane przez inny element sterujący
(kontrolkę), który jest zawarty w kontrolce DataList. Właściwość CommandName tego
elementu musi mieć wartość Update.
 OnDeleteCommand - to zdarzenie może być inicjowane przez inny element sterujący
(kontrolkę), który jest zawarty w kontrolce DataList. Właściwość CommandName tego
elementu musi mieć wartość Delete.
 OnCancelCommand - to zdarzenie może być inicjowane przez inny element sterujący
(kontrolkę), który jest zawarty w kontrolce DataList. Właściwość CommandName tego
elementu musi mieć wartość Cancel.
W listingu Wydruk0910.aspx są pokazane kody HTML strony z kontrolką DataList. Nie
wolno zapominać o umieszczeniu obiektu DataList pomiędzy znacznikami <form>. W
listingu Wydruk0910.aspx.vb jest pokazany kod klasy programowej.
Listing Wydruk0910.aspx
1. <%@ Page Language="vb" Debug="true" CodeBehind="wydruk0910.aspx.vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.wydruk0910" %>
2. <HTML>
3. <body>
4.
<form runat="server">
5.
<asp:DataList id="DataList1" runat="server" SelectedItemStyleBackColor = "#cccc99" repeatlayout="table" repeatdirection="horizontal"
6. OnItemCommand="DataList1_ItemCommand" DataKeyField="UserID">
7.
<ItemTemplate>
8.
<asp:LinkButton id="button1" runat="server"
96
9.
Text='<%# Container.DataItem("FirstName") & " " & _
10.
Container.DataItem("LastName") %>'
11.
CommandName="Select"/>
12.
</asp:LinkButton>
13.
</ItemTemplate>
14.
<SelectedItemTemplate>
15.
<%# Container.DataItem("FirstName") & " " & _
16.
Container.DataItem("LastName") %>
17.
<br>
18.
Telefon:
19.
<%# Container.DataItem("Phone") %>
20.
<br>
21.
</SelectedItemTemplate>
22.
</asp:DataList>
23.
</form>
24. </body>
25. </HTML>
W liniach 5-22 jest wyznaczona kontrolka DataList z identyfikatorem DataList1. W linii 5
jest wyznaczony tryb prezentacji repeatlayout="table" oraz
sposób rozmieszczenia
rekordów repeatdirection="horizontal". W linii 6 jest wyznaczony klucz sortowania
rekordów DataKeyField="UserID" oraz wyznaczone zdarzenie OnItemCommand.
Program dla tego zdarzenia ma nazwę DataList1_ItemCommand. Zdarzenie jest
powodowane przez kontrolkę LinkButton, która jest rozmieszczona w szablonie
ItemTemplate w liniach 7-13. W tym szablonie są wyznaczone pola rekordów FirstName
oraz LastName , które będą
wyświetlane w normalnym trybie. W szablonie
<SelectedItemTemplate> są wyznaczone pola rekordów, którzy będą wyświetlane po
klikniejciu LinkButton.
Listing Wydruk0910.aspx.vb
1.
2.
3.
4.
5.
6.
7.
Imports System.Data.OleDb
Imports System.Data
Public Class wydruk0910
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
8. Dim MojePolaczenie As New OleDbConnection _
9. ("Provider=Microsoft.Jet.OLEDB.4.0;" & _
10.
"Data Source=C:\aspnet_bazy_test\banking.mdb")
'otwarcie polaczenia
11.
Dim MojePolecenie As New OleDbDataAdapter _
12.
("select * from tblUsers", MojePolaczenie)
'wypelnienie obiektu DataSet
13.
Dim ds As DataSet = New DataSet
14.
MojePolecenie.Fill(ds, "tbl")
'wybor widoku danych i zwiazanie z obiektem sterujacym
15.
DataList1.DataSource = ds.Tables("tbl").DefaultView
16.
DataBind()
17.
End Sub
18.
Sub DataList1_ItemCommand(ByVal obj As Object, ByVal e As
DataListCommandEventArgs)
19.
DataList1.SelectedIndex = e.Item.ItemIndex
20.
DataList1.DataBind()
21.
End Sub
97
22. End Class
Edytowanie wyświetlanych pozycji w kontrolce DataList
Kontrolka DataList umożliwia również edytowanie wyświetlanych pozycji. W Listingu
SqlDataListEdit.aspx są pokazane definicje kontrolki DataList oraz szablonów. W Listingu
SqlDataListEdit.aspx.vb są pokazany kody klasy programowej , którzy są wykorzystane dla
realizacji niezbędnych zdarzeń w celu edycji wyświetlanych danych.
W szablonie ItemTemplate dla kontrolki LinkButton w linii 16 jest wyznaczona właściwość
CommandName = Edit. W tym przypadku przy klikniejciu dowolnego linku kontrolki
ListButton będzie wywołane zdarzenie OnEditCommand. Z tym zdarzeniem jest skojarzona
procedura editAuthor (patrz linie 12 kodu SqlDataListEdit.aspx). Procedura editAuthor (patrz
linie 21-24 kodu SqlDataListEdit.aspx.vb) przywłaszcza indeks wybranego autora
właściwości EditItemIndex kontrolki DataList. Kolejne wywołanie kontrolki DataList na
ekran monitora powoduje wykorzystanie dla tego rekordu już szablonu EditItemTemplate, a
nie zwyczajnego szablony ItemTemplate. Szablon EditItemTemplate zawiera 3 kontrolki
TextBox dla nazwiska, imię oraz telefonu autora. Ten szablon zawiera oraz 3 przyciski:
Upddate, Delete, Cancel. Kiedy użytkownik naciśnie Update będzie wywołane zdarzenie
OnUpdateCommand oraz procedurę updateAuthor (patrz linie 48-80 kodu
SqlDataListEdit.aspx.vb). Procedura updateAuthor modyfikuje informację pro autora w bazie
danych. W tym celu jest wykorzystany objekt myCommand klasy SQLCommand (linia 57).
Dla obiektu myCommand są wyznaczone parametry @Lastname, @firstname, @phone w
liniach 58-73. Wartość, która została wpisana przez użytkownika do kontrolki TextBox, z
identyfikatorem Lastname zostanie przepisana do parametru @lastname w sposób następny:
myCommand.Parameters("@lastname").Value =
CType(e.Item.FindControl("lastname"), TextBox).Text
Metoda systemowa Ctype() jest wykorzystana, żeby powiązać obiekt e z obiektem typu
TextBox. Kompilator .NET nie może zrealizować pożnie wiązanie obiektów i danych, dlatego
on musi wiedzieć, że obiekt, który powodował zdarzenie jest obiektem typu TextBox i
zawiera właściwość Text. Obiekt, który powodował zdarzenie jest wyznaczony za dopomogą
metody FindControl().
Listing SqlDataListEdit.aspx.
2. <%@ Page CodeBehind="SqlDataListEdit.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataListEdit" %>
3. <HTML>
4. <HEAD>
5.
<title>Edit Authors</title>
6. </HEAD>
7. <body>
8.
<form Runat="Server">
9.
<asp:DataList id="myDataList" cellpadding="10" cellspacing="0"
10.
gridlines="both" RepeatColumns="3" RepeatDirection="Horizontal"
11.
DataKeyField="au_id"
12.
OnEditCommand="editAuthor" OnDeleteCommand="deleteAuthor"
OnUpdateCommand="updateAuthor"
13.
OnCancelCommand="cancelEdit"
14.
Runat="Server">
15.
<ItemTemplate>
16.
<asp:LinkButton Text="Edit" CommandName="edit" Runat="Server" />
17.
<%# Container.DataItem( "au_lname" )%>
98
18.
</ItemTemplate>
19.
<EditItemTemplate>
20.
<b>Last Name:</b>
21.
<br>
22.
<asp:TextBox id="lastname"
23.
text='<%# Container.DataItem( "au_lname" ) %>'
24.
Runat="Server"/>
25.
<p>
26.
<b>First Name:</b>
27.
<br>
28.
<asp:TextBox id="firstname"
29.
text='<%# Container.DataItem( "au_fname" ) %>'
30.
Runat="Server"/>
31.
<p>
32.
<b>Phone:</b>
33.
<br>
34.
<asp:TextBox id="phone"
35.
text='<%# Container.DataItem( "phone" ) %>' Runat="Server"/>
36.
<p>
37.
<asp:Button Text="Update" CommandName="update"
38.
Runat="Server" />
39.
<asp:Button Text="Delete" CommandName="delete"
40.
Runat="Server" />
41.
<asp:Button Text="Cancel" CommandName="cancel"
42.
Runat="Server" />
43.
</EditItemTemplate>
44.
</asp:DataList>
45.
</form>
46. </body>
47. </HTML>
Listing SqlDataListEdit.aspx.vb.
1.
2.
3.
4.
5.
Imports System.Data
Imports System.Data.SqlClient
Public Class SqlDataListEdit Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
#End Region
6. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
7.
If Not IsPostBack Then
8.
BindData()
9.
End If
10.
End Sub
11.
Sub BindData()
12.
Dim myConnection As SqlConnection
13.
Dim myCommand As SQLCommand
14.
myConnection = New
SqlConnection("Server=Localhost;uid=sa;pwd=;Database=Pubs")
15.
myCommand = New SqlCommand("Select au_id, au_lname,
au_fname, phone from Authors order by au_lname", myConnection)
16.
myConnection.Open()
17.
myDataList.DataSource = myCommand.ExecuteReader()
18.
myDataList.DataBind()
19.
myConnection.Close()
99
20.
End Sub
21.
Sub editAuthor(ByVal s As Object, ByVal e As
DataListCommandEventArgs)
22.
myDataList.EditItemIndex = e.Item.ItemIndex
23.
BindData()
24.
End Sub
25.
Sub cancelEdit(ByVal s As Object, ByVal e As
DataListCommandEventArgs)
26.
myDataList.EditItemIndex = -1
27.
BindData()
28.
End Sub
29.
Sub deleteAuthor(ByVal s As Object, ByVal e As
DataListCommandEventArgs)
30.
Dim myConnection As SqlConnection
31.
Dim myCommand As SqlCommand
32.
Dim sqlString As String
33.
myConnection = New
34.
SqlConnection("Server=Localhost;uid=sa;pwd=;Database=Pubs")
35.
sqlString = "Delete Authors Where au_id=@authorID"
36.
myCommand = New SqlCommand(sqlString, myConnection)
37.
myCommand.Parameters.Add(New SQLParameter("@authorID",
38.
SqlDbType.VarChar, 11))
39.
myCommand.Parameters("@authorID").Value =
40.
myDataList.DataKeys.Item(e.Item.ItemIndex)
41.
myConnection.Open()
42.
myCommand.ExecuteNonQuery()
43.
myDataList.DataBind()
44.
myConnection.Close()
45.
myDataList.EditItemIndex = -1
46.
BindData()
47.
End Sub
48.
Sub updateAuthor(ByVal s As Object, ByVal e As
DataListCommandEventArgs)
49.
Dim myConnection As SQLConnection
50.
Dim myCommand As SQLCommand
51.
Dim sqlString As String
52.
myConnection = New
53.
SqlConnection("Server=Localhost;uid=sa;pwd=;Database=Pubs")
54.
sqlString = "Update Authors Set au_lname=@lastname,
55.
au_fname=@firstname, phone=@phone" _
56.
& " Where au_id=@authorID"
57.
myCommand = New SQLCommand(sqlString, myConnection)
58.
myCommand.Parameters.Add(New SQLParameter("@lastname",
59.
SqlDbType.VarChar, 40))
60.
myCommand.Parameters("@lastname").Value =
61.
CType(e.Item.FindControl("lastname"), TextBox).Text
62.
myCommand.Parameters.Add(New SQLParameter("@firstname",
63.
SqlDbType.VarChar, 20))
64.
myCommand.Parameters("@firstname").Value =
65.
CType(e.Item.FindControl("firstname"), TextBox).Text
66.
myCommand.Parameters.Add(New SQLParameter("@phone",
67.
SqlDbType.Char, 12))
68.
myCommand.Parameters("@phone").Value =
69.
CType(e.Item.FindControl("phone"), TextBox).Text
70.
myCommand.Parameters.Add(New SQLParameter("@authorID",
71.
SqlDbType.VarChar, 11))
72.
myCommand.Parameters("@authorID").Value =
100
73.
74.
75.
76.
77.
78.
79.
80.
myDataList.DataKeys.Item(e.Item.ItemIndex)
myConnection.Open()
myCommand.ExecuteNonQuery()
myDataList.DataBind()
myConnection.Close()
myDataList.EditItemIndex = -1
BindData()
End Sub
End Class
Przykład stworzenia aplikacji z kontrolką DataList w środowisku MS
Visual Studio.
W tym przykładzie będą wykorzystane narzędzie MS Visual Studio dla stworzenia strony
internetowej. Projektant nie musi wpisywać samodzielnie wszystkie potrzebne linii kodu.
Strona musi odwołać się do bazy danych MS SQL Server 2000 oraz wyświetlić z bazy danych
Pub, tabeli Authors nazwiska autorów. Po klikniejciu dowolnego autora w tym mieście musi
być wyświetlona dodatkowa informacja z bazy danych (np. telefon autora).
Stworzenie aplikacji w Visual Studio zawiera następne 26 kroków:
1. Przenieść kontrolkę DataList do panelu projektu strony Rys. 24
Rys. 24
2. Przenieść kontrolkę SqlDataAdapter do panelu projektu . Będzie wyświetlane okno
Rys. 25, które jest przeznaczone dla konfigurowania kontrolki i stworzenia innych
kontrolek. Kliknij Next.
101
Rys.25
3. Wizard konfigurowania proponuje w oknie rys.26 wyznaczyć sposób połączenia z bazą
danych. Można wyznaczyć już istniejące połączenie. Wybierz New Connection.
Rys.26
102
4. W następnym oknie rys.27 ustal parametry łącza z bazą danych.
Rys.27
5. Kliknij „Testuj Połączenie”, a potem OK (rys.28).
Rys.28
103
6.Kliknij Next (Rys.29)
Rys.29
7. Ustal dostęp do bazy danych za dopomogą polecenia SQL (Rys.30).
Rys.30
104
8. Kiedy nie chcesz (lub nie możesz!) samodzielnie wpisywać kod SQL uruchom Query
Builder (rys.31). Wybierz niezbędne tabeli do konstruowania polecenia SQL i zamknij okno
Add Table.
Rys. 31
9. W oknie Query Builder (Rys.32)wyznacz niezbędne pola dla polecenia SQL. Kliknij Ok.
105
Rys.32
10. Kliknij Next.
106
Rys.33
11. Kliknij Finish (rys.34)
Rys. 34
12. Dla stworzenia obiektu DataSet odtwórz okno Propeties obiektu SqlDataAdapter (rys.35).
Kliknij link „Generacja DataSet”
107
Rys.35
13. Popatrz właściwości nowego obiektu DataSet (rys.36). Została stworzona klasa DataSet2
w projekcie aplikacji. Na stronie implementacja tej klasy ma imię DataSet21. To nie jest
koniecznie , można ustalić dowolne imię dla obiektu DataSet.
108
Rys.36
15. Dla obiektu DataList uruchom Properties-> Property Builder (rys.37). Ustal właściwości
Data key field, Direction, Layout, DataSource.
Rys.37
109
16. Ustal przez menu kontekstowe tryb edycji Item Template (Rys 38).
Rys.38
17. Przenieś kontrolkę LinkButton w pole Item Template (Rys.39)
Rys. 39
18. Uruchom okno właściwości kontrolki LinkButton oraz kliknij przycisk „...” w polu
DataBinds (rys. 40)
110
Rys. 40
19. W oknie DataBindings (Rys.41) ustal Bindable Properties = „Text”, oraz Custom binding
expression. W oknie Custom binding expression wpisz kod:
DataBinder.Eval(Container, "DataItem.au_id", "{0}") +" "+
DataBinder.Eval(Container,"DataItem.au_lname","{0}")
Rys.41
Kliknij OK.
111
20. Przejdź do edycji pola SelectedItemTemplate (rys.42). W sposób analogiczny ustal
kontrolkę LinkButton w pole SelectedItemTemplate.
Rys. 42
21. Uruchom okno Label1 DataBindings (Rys.43). W pole Custom binding expression wpisz
kod:
DataBinder.Eval(Container, "DataItem.au_id", "{0}")+" " +DataBinder.Eval(Container,
"DataItem.au_lname", "{0}")+" " +"<br>Phone:"+DataBinder.Eval(Container,
"DataItem.phone", "{0}")+"<br>"
Kliknji Ok.
Rys. 43
22. Wpisz dwie linie kodu do procedury Page_Load:
112
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
Me.SqlDataAdapter1.Fill(Me.DataSet31)
Me.DataList1.DataBind()
End Sub
26. Wpisz dwie linie kodu do procedury DataList1_ItemCommand1:
Private Sub DataList1_ItemCommand1(ByVal source As Object, ByVal e As
System.Web.UI.WebControls.DataListCommandEventArgs) Handles
DataList1.ItemCommand
DataList1.SelectedIndex = e.Item.ItemIndex
DataList1.DataBind()
End Sub
Wykorzystanie kontrolki DataGrid.
Kontrolka DataGrid ma podobne zastosowanie, jak kontrolki DataList i Repeater, ale ma
jeszcze więcej funkcji. DataGid może być używany w trybach prezentacji danych kontrolki
Repeater lub edycji danych kontrolki DataList. DataGrid zawiera dodatkowe możliwości
które są nieobecne w kontrolkach DataList oraz Repeater. DataGrid pozwoli zrealizować
sortowanie i stronicowanie prezentowanych danych. Za pomocą DataGrid dane wyświetlane
są w postaci siatki. Domyślne kontrolka DataGrid tworzy kolumnę dla każdego pola z
odwzorowanego magazynu danych. Programista może jednak określić, które pola mają być
wyświetlane, jak również sposób ich przedstawienia. Można zdefiniować następujące rodzaje
kolumn:
 Bound column (kolumny związane) – w tym przypadku należy określić. Które kolumny i
w jakiej kolejności mają być wyświetlone, oraz podać styl wyświetlania. Jest to domyślny
sposób wyświetlania kolumn przez kontrolkę DataGrid.
 Button column (kolumny przycisków) – w tym przypadku dla wszystkich pozycji wierszy
siatki wyświetlane są przyciski, którym przypisane są odpowiednie funkcje.
 Edit Command column (kolumny z poleceniami edycyjnymi) – umożliwiają edytowanie
danej pozycji. Zamieniają wszystkie kolumny ograniczone na pola modyfikowane.
 Hyperlink column - wyświetlają dane w postaci hiperlinków.
 Templated column (szablony kolumn) – tak samo jak w przypadku kontrolki Repeater i
DataList, można okreslić sposób wyświetlania kolumn, dostosowany do konkretnych
potrzeb.
Przykład wykorzystania DataGrid dla odwzorowania wierszy i kolumn tabeli bazy danych w
sposób domyślny jest pokazany w listingu SqlDataGrid.aspx.
Listing SqlDataGrid.aspx.
1. <%@
Page
CodeBehind="SqlDataGrid.aspx.vb"
Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGrid" %>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:DataGrid id="myDataGrid" Runat="Server" />
9.
</form>
10. </body>
11. </HTML>
Kod klasy programowej jest pokazany w listingu SqlDataGrid.aspx.vb.
Listing SqlDataGrid.aspx.vb.
113
1. Public Class SqlDataGrid
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6.
Dim myConnection As System.Data.SqlClient.SqlConnection
7.
Dim myCommand As System.Data.SqlClient.SqlCommand
8.
myConnection = New System.Data.SqlClient.SqlConnection
"Server=Localhost;uid=sa;pwd=;Database=Northwind")
9.
myCommand = New System.Data.SqlClient.SqlCommand ("Select *
from Products", myConnection)
10.
myConnection.Open()
11.
myDataGrid.DataSource = myCommand.ExecuteReader()
12.
myDataGrid.DataBind()
13.
myConnection.Close()
14.
End Sub
15. End Class
Wykorzystanie kontrolki DataGrid w tych listingach jest podobnym trybu kontrolki Repeater
oraz nie wykorzysta zaawansowanych możliwości prezentacji danych.
Odwzorowanie kolumn w kontrolce DataGrid
Przy prezentacji danych w kontrolce DataGrid jest możliwość wyeliminować oddzielnie
kolumny i w ten sposób sterować procesem odwzorowania. Kontrolka DataGrid ma
możliwość definicji kolumn które trzeba odwzorować. W tym celu trzeba przypisać
właściwości AutoGenerateColumns = false oraz wyznaczyć kolumny do prezentacji. Kod
listingu SqlDataGridBoundCols.aspx zawiera deklarację kontrolki DataGrid oraz je obiektów
kolumn.
Listing SqlDataGridBoundCols.aspx.
1. <%@ Page CodeBehind="SqlDataGridBoundCols.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGridBoundCols" %>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:DataGrid id="myDataGrid" AutoGenerateColumns="False"
9.
Runat="Server">
10.
<Columns>
11.
<asp:BoundColumn HeaderText="Product Name"
12.
DataField="ProductName" />
13.
<asp:BoundColumn HeaderText="Price"
14.
DataField="UnitPrice" DataFormatString="{0:c}" />
15.
</Columns>
16.
</asp:DataGrid>
17.
</form>
18. </body>
19. </HTML>
Listing SqlDataGridBoundCols.aspx.vb zawiera kod klasy pośredniej.
Listing SqlDataGridBoundCols.aspx.vb.
1. Public Class SqlDataGridBoundCols
2. Inherits System.Web.UI.Page
114
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6.
Dim myConnection As System.Data.SqlClient.SqlConnection
7.
Dim myCommand As System.Data.SqlClient.SqlCommand
8.
myConnection = New
System.Data.SqlClient.SqlConnection
9.
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
10.
myCommand = New System.Data.SqlClient.SqlCommand
11.
myConnection.Open()
12.
myDataGrid.DataSource = myCommand.ExecuteReader()
13.
myDataGrid.DataBind()
14.
myConnection.Close()
15.
End Sub
16. End Class
W linii 12 do właściwości DataSource obiektu DataGrid został przypisany obiekt DataRead.
Dane przez obiekt DataRead zostaną odczytane z bazy danych. Kolumny do wyświetlania
zostali zdefiniowane w liniach 10-15 listingu SqlDataGridBoundCols.aspx.
Wykorzystując właściwość Visible każdej kolumny kontrolki DataGrid można robić
widocznymi lub niewidocznymi odpowiednie kolumny. W listingu SqlDataGridVisible.aspx
jest pokazany przykład strony zawierającej obiekt DataGrid oraz dwa przyciski: „Show
Details” oraz „Hide Details”. Przy pierwszym uruchomieniu strony zostaną niewidoczne
kolumny UnitPrice oraz QuantityPerUnit z tabeli bazy danych Northwind. Przy klikniejciu
Show Details te kolumny będą widoczne.
Listing SqlDataGridVisible.aspx.
1. <%@ Page CodeBehind="SqlDataGridVisible.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGridVisible" %>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:Button Text="Show Details" Runat="server" id="Show" />
9.
<asp:Button Text="Hide Details" Runat="server" id="Hide" />
10.
<asp:DataGrid id="myDataGrid" AutoGenerateColumns=
11.
"False" Runat="Server">
12.
<Columns>
13.
<asp:BoundColumn HeaderText="Product Name"
14.
DataField="ProductName" />
15.
<asp:BoundColumn HeaderText="Price"
16.
DataField="UnitPrice" DataFormatString="{0:c}"
17.
Visible="False" />
18.
<asp:BoundColumn HeaderText="Quantity Per Unit"
19.
DataField="QuantityPerUnit" Visible="False" />
20.
</Columns>
21.
</asp:DataGrid>
22.
</form>
23. </body>
115
24. </HTML>
Kod klasy programowej jest pokazany w listingu SqlDataGridVisible.aspx.vb.
Listing SqlDataGridVisible.aspx.vb.
1. Public Class SqlDataGridVisible
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6.
If Not IsPostBack Then
7.
Me.BindData()
8.
End If
9. End Sub
10.
Sub BindData()
11.
Dim myConnection As System.Data.SqlClient.SqlConnection
12.
Dim myCommand As System.Data.SqlClient.SqlCommand
13.
myConnection = New System.Data.SqlClient.SqlConnection _
14.
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
15.
myCommand = New System.Data.SqlClient.SqlCommand _
16.
("Select * from Products", myConnection)
17.
myConnection.Open()
18.
myDataGrid.DataSource = myCommand.ExecuteReader()
19.
myDataGrid.DataBind()
20.
myConnection.Close()
21.
End Sub
22.
Private Sub Show_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Show.Click
23.
Dim colCounter As Integer
24.
For colCounter = 1 To myDataGrid.Columns.Count - 1
25.
myDataGrid.Columns(colCounter).Visible = True
26.
Next
27.
End Sub
28.
Private Sub Hide_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Hide.Click
29.
Dim colCounter As Integer
30.
For colCounter = 1 To myDataGrid.Columns.Count - 1
31.
myDataGrid.Columns(colCounter).Visible = False
32.
Next
33.
End Sub
34.
End Class
Kod klasy pośrednie zawiera metody obrabiania zdarzeń Show_Click oraz Hide_Click. W
tych metodach za dopomogą pętli For...Next ustali się właściwość Visible wszystkich kolumn
oprócz kolumny z indeksem 0.
Edytowanie danych w kontrolce DataGrid
Kontrolka DataGrid pozwala na edycję oddzielnych rekordów. Żeby udostępnić rekord do
edytowania trzeba przypisać właściwości EditItem kontrolki DataGrid indeks odpowiedniego
rekordu który chcemy edytować. W tym przypadku tekst tego rekordu w kontrolce DataGrid
będzie odwzorowany przez obiekt sterujący TextBox, który będzie automatyczne wdrożony
do wszystkich możliwych pól edycji tego rekordu.
Przykład wykorzystania DataGrid do edycji rekordów tabeli Products bazy danych Northwind
jest pokazany w listingu SqlDataGridEdit.aspx.
Listing SqlDataGridEdit.aspx.
116
1. <%@
Page
CodeBehind="SqlDataGridEdit.aspx.vb"
Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGridEdit" %>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:datagrid id="myDataGrid" Runat="Server" DataKeyField="ProductID"
9.
AutoGenerateColumns="False" cellpadding="3">
10.
<Columns>
11.
<asp:EditCommandColumn EditText="Edit" CancelText="Cancel"
12.
UpdateText="Update" />
13.
<asp:BoundColumn HeaderText="Product ID"
14.
DataField="ProductID" ReadOnly="True" />
15.
<asp:BoundColumn HeaderText="Product Name"
16.
DataField="ProductName" />
17.
<asp:BoundColumn HeaderText="Price"
18.
DataField="UnitPrice" DataFormatString="{0:c}" />
19.
</Columns>
20.
</asp:datagrid>
21.
</form>
22. </body>
23. </HTML>
W liniach 11,12 kodu SqlDataGridEdit.aspx została zdefiniowana kolumna
EditCommandColumn która automatyczne formuje kontrolkę LinkButton dla operacji edycji.
Kontrolka LinkButton może być zamieniona na zwykłą Button (PushButton) przez ustalenie
właściwości Button Type kolekcji Column. Kiedy rekord do edycji jeszcze nie został
wybrany, kolumna EditCommandColumn formuje na ekranie przeglądarki element
LinkButton z tekstem "Edit". Po kliknięciu na ten przycisk kolumna EditCommandColumn
formuje kontrolki LinkButton Update oraz Cancel.
Kod klasy pośredniej jest pokazany w Listingu SqlDataGridEdit.aspx.vb.
Kiedy po modyfikacji danych użytkownik kliknji Update będzie uruchomiana procedura
myDataGrid_UpdateCommand()która odpowiada zdarzeniu UpdateCommand kontrolki
DataGrid. Dla ustalenia nowych wartości kolumn ProductName oraz UnitPrice są
wykorzystane linie kodu 28-31. W liniach 33, 34 zostali zadeklarowane obiekty klas
SqlConnection oraz SqlCommand. W liniach 36,37 są wyznaczone parametry połączenia z
bazą danych Northwind. W liniach 38-40 jest wyznaczona instrukcja SQL Update z
parametrami do bazy danych. W liniach 41-57 wyznaczone są obiekt SqlCommand oraz go
parametry . Po zakończeniu procedury trzeba skasować indeks rekordu aktualnej edycji,
dlatego w linii 61 mamy kod:
myDataGrid.EditItemIndex = -1
Listing SqlDataGridEdit.aspx.vb.
1. Public Class SqlDataGridEdit
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
117
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6.
If Not IsPostBack Then
7.
BindData()
8.
End If
9. End Sub
10.
Sub BindData()
11.
Dim myConnection As System.Data.SqlClient.SqlConnection
12.
Dim myCommand As System.Data.SqlClient.SqlCommand
13.
myConnection = New System.Data.SqlClient.SqlConnection _
14.
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
15.
myCommand = New System.Data.SqlClient.SqlCommand( _
16.
"Select * from Products", myConnection)
17.
myConnection.Open()
18.
myDataGrid.DataSource = myCommand.ExecuteReader()
19.
myDataGrid.DataBind()
20.
myConnection.Close()
21.
End Sub
22.
Private Sub myDataGrid_EditCommand(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
Handles myDataGrid.EditCommand
23.
myDataGrid.EditItemIndex = e.Item.ItemIndex
24.
BindData()
25.
End Sub
26.
Private Sub myDataGrid_UpdateCommand(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
Handles myDataGrid.UpdateCommand
27.
' Get New Values
28.
Dim productName As TextBox
29.
productName = e.Item.Cells(2).Controls(0)
30.
Dim unitPrice As TextBox
31.
unitPrice = e.Item.Cells(3).Controls(0)
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
' Update Database
Dim myConnection As System.Data.SqlClient.SqlConnection
Dim myCommand As System.Data.SqlClient.SqlCommand
Dim sqlString As String
myConnection = New System.Data.SqlClient.SqlConnection _
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
sqlString = "Update Products Set ProductName = " & _
"@productName, UnitPrice=@unitPrice Where _
ProductID=@productID"
myCommand = New System.Data.SqlClient.SqlCommand _
(sqlString, myConnection)
myCommand.Parameters.Add ( _
New System.Data.SqlClient.SqlParameter("@ProductName", _
SqlDbType.NVarChar, 80))
myCommand.Parameters("@ProductName").Value = _
productName.Text
myCommand.Parameters.Add( _
New System.Data.SqlClient.SqlParameter("@unitprice", _
SqlDbType.Money))
myCommand.Parameters("@unitPrice").Value = _
CType(unitPrice.Text, Decimal)
myCommand.Parameters.Add( _
New System.Data.SqlClient.SqlParameter("@productID", _
SqlDbType.Int))
myCommand.Parameters("@productID").Value = _
myDataGrid.DataKeys.Item(e.Item.ItemIndex)
118
58.
59.
60.
61.
62.
63.
myConnection.Open()
myCommand.ExecuteNonQuery()
myConnection.Close()
myDataGrid.EditItemIndex = -1
BindData()
End Sub
64.
Private Sub myDataGrid_CancelCommand(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
Handles myDataGrid.CancelCommand
65.
myDataGrid.EditItemIndex = -1
66.
BindData()
67.
End Sub
68.
End Class
Wykorzystanie szablonów w kontrolce DataGrid
Wykorzystanie szablonów pozwała oprócz bezpośredniego wyświetlania powiązanych
danych zrealizować szereg czynności po sprawdzeniu odwzorowanych danych oraz po
formowaniu wyglądu prezentowanej strony. W poprzednim przykładzie nie było możliwości
walidować wprowadzone dane. Np. zamiast do pola UnitPrice można byłoby wpisać
niekompatybilny typ danych. Szablony pozwalają zdefiniować kontrolki walidatorów danych
oraz jakiekolwiek inny typy kontrolek .NET FRAMEWORK do kontroli oraz prezentacji
odpowiednich kolumn DataGrid.
W listingu SqlDataGridEditTemplate.aspx jest pokazany kod umożliwiający modyfikowania
danych poprzedniego przykładu z wykorzystaniem szablonów.
Listing SqlDataGridEditTemplate.aspx.
1. <%@ Page CodeBehind="SqlDataGridEditTemplate.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGridEditTemplate"
%>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:DataGrid id="myDataGrid" cellpadding="3"
9.
AutoGenerateColumns="False" DataKeyField="ProductID"
10.
Runat="Server">
11.
<Columns>
12.
<asp:EditCommandColumn EditText="Edit" CancelText="Cancel"
13.
UpdateText="Update" />
14.
<asp:TemplateColumn>
15.
<ItemTemplate>
16.
<%# Container.DataItem( "ProductID" )%>
17.
</ItemTemplate>
18.
</asp:TemplateColumn>
19.
<asp:TemplateColumn>
20.
<ItemTemplate>
21.
<%# Container.DataItem( "ProductName" )%>
22.
</ItemTemplate>
23.
<EditItemTemplate>
24.
<asp:TextBox id="ProductName"
25.
Text='<%# Container.DataItem( "ProductName" )%>'
119
26.
Runat="Server"/>
27.
<asp:RequiredFieldValidator
28.
ControlToValidate="ProductName"
29.
Display="Dynamic" Runat="Server">
30.
You must enter a product name!
31.
</asp:RequiredFieldValidator>
32.
</EditItemTemplate>
33.
</asp:TemplateColumn>
34.
<asp:TemplateColumn>
35.
<ItemTemplate>
36.
<%# String.Format( "{0:c}",
37.
Container.DataItem( "UnitPrice" ) ) %>
38.
</ItemTemplate>
39.
<EditItemTemplate>
40.
<asp:TextBox id="UnitPrice"
41.
Text='<%# Container.DataItem( "UnitPrice" )%>'
42.
Runat="Server"/>
43.
<asp:RequiredFieldValidator ControlToValidate="UnitPrice"
44.
Display="Dynamic" Runat="Server">
45.
You must enter a unit price!
46.
</asp:RequiredFieldValidator>
47.
<asp:CompareValidator ControlToValidate="UnitPrice"
48.
Display="Dynamic" Type="Currency"
49.
Operator="DataTypeCheck"
50.
Runat="Server">
51.
The unit price must be a money amount!
52.
</asp:CompareValidator>
53.
</EditItemTemplate>
54.
</asp:TemplateColumn>
55.
</Columns>
56.
</asp:DataGrid>
57.
</form>
58. </body>
59. </HTML>
W tym listingu została wykorzystana klasa TemplateColumn lecz nie BoundColumn jako
w poprzednim przykładzie. Klasa BoundColumn może być przekształcona do klasy
TemplateColumn w środowisku VSNet. Klasa TemplateColumn może zawierać szablon
ItemTemplate oraz szablon EditTemplate. Szablon EditTemplate odwzorowuje rekord
kontrolki DataGrid przy wybieraniu tego rekordu do edycji. W liniach 23-32 jest
wyznaczony szablon EditTemplate do edycji pola ProductName. Dla odwzorowania
danych z pola ProductName jest wyznaczona kontrolka TextBox w liniach 24-26.
Kontrola
wprowadzonych
danych
realizuje
się
przez
walidator
typu
RequiredFieldValidator , który został zdefiniowany w liniach 27-31. W sposób podobny
jest wyznaczony szablon do edycji pola UnitPrice w liniach 39-53.
W listingu SqlDataGridEditTemplate.aspx.vb jest pokazany kod klasy pośredniej tej
strony.
Listing SqlDataGridEditTemplate.aspx.vb.
1. Public Class SqlDataGridEditTemplate
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
120
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6. If Not IsPostBack Then
7.
BindData()
8. End If
9. End Sub
10.
Sub BindData()
11.
Dim myConnection As System.Data.SqlClient.SqlConnection
12.
Dim myCommand As System.Data.SqlClient.SqlCommand
13.
myConnection = New
14.
System.Data.SqlClient.SqlConnection _
15.
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
16.
myCommand = New System.Data.SqlClient.SqlCommand _
17.
("Select * from Products", _
18.
myConnection)
19.
myConnection.Open()
20.
myDataGrid.DataSource = myCommand.ExecuteReader()
21.
myDataGrid.DataBind()
22.
myConnection.Close()
23.
End Sub
24.
Private Sub myDataGrid_EditCommand(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
Handles myDataGrid.EditCommand
25.
myDataGrid.EditItemIndex = e.Item.ItemIndex
26.
BindData()
27.
End Sub
28.
Private Sub myDataGrid_UpdateCommand(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
Handles myDataGrid.UpdateCommand
29.
If IsValid Then
30.
' Get New Values
31.
Dim productName As TextBox
32.
productName = e.Item.FindControl("ProductName")
33.
Dim unitPrice As TextBox
34.
unitPrice = e.Item.FindControl("UnitPrice")
35.
36.
37.
38.
' Update Database
Dim myConnection As System.Data.SqlClient.SqlConnection
Dim myCommand As System.Data.SqlClient.SqlCommand
Dim sqlString As String
39.
40.
41.
42.
43.
myConnection = New System.Data.SqlClient.SqlConnection
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
sqlString = "Update Products Set ProductName=@productName, "_&
"UnitPrice=@unitPrice Where ProductID=@productID"
myCommand = New System.Data.SqlClient.SqlCommand(sqlString,
myConnection)
44.
myCommand.Parameters.Add( _
45.
New System.Data.SqlClient.SqlParameter("@ProductName",
SqlDbType.NVarChar, 80))
46.
myCommand.Parameters("@ProductName").Value = _
47.
productName.Text
48.
myCommand.Parameters.Add( _
49.
New System.Data.SqlClient.SqlParameter("@unitprice",
SqlDbType.Money))
50.
myCommand.Parameters("@unitPrice").Value = _
51.
CType(unitPrice.Text, Decimal)
52.
myCommand.Parameters.Add( _
121
53.
New System.Data.SqlClient.SqlParameter("@productID",
SqlDbType.Int))
54.
myCommand.Parameters("@productID").Value = _
55.
myDataGrid.DataKeys.Item(e.Item.ItemIndex)
56.
myConnection.Open()
57.
myCommand.ExecuteNonQuery()
58.
myConnection.Close()
59.
myDataGrid.EditItemIndex = -1
60.
BindData()
61.
End If
62.
End Sub
63.
Private Sub myDataGrid_CancelCommand(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
Handles myDataGrid.CancelCommand
64.
myDataGrid.EditItemIndex = -1
65.
BindData()
66.
End Sub
67.
End Class
Ten kod jest podobny do kodu poprzedniego przykładu.
Sortowanie danych w kontrolce DataGrid
Kontrolka DataGrid zawiera właściwość AllowSorting która pozwała na sortowanie po
kolumnach. Kiedy AllowSorting = true oraz AutoGenerateColumns = true, DataGrid
wyświetla automatyczne wszystkie możliwe sortowania kolumn w postaci przycisków
LinkButton. Kliknięjcie przyciska powoduje zdarzenie klasy SortCommand oraz wywołanie
pocedury myDataGrid_SortCommand (). W listingu SqlDataGridSort.aspx jest pokazany
kod strony aspx z definicją obiektu DataGrid.
Listing SqlDataGridSort.aspx
1. <%@ Page CodeBehind="SqlDataGridSort.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGridSort" %>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:DataGrid id="myDataGrid" AllowSorting="True" cellpadding="3"
9.
Runat="Server" />
10.
</form>
11. </body>
12. </HTML>
W listingu SqlDataGridSort.aspx.vb jest pokazany kod klasy pośredniej, zawierającej logikę
połączenia z bazą danych oraz opracowania zdarzeń.
Listing SqlDataGridSort.aspx.vb.
1. Public Class SqlDataGridSort
2. Inherits System.Web.UI.Page
3.
4.
5.
6.
#Region " Web Form Designer Generated Code "
#End Region
Dim sortField As String = "ProductID"
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
7.
If Not IsPostBack Then
8.
BindData()
9.
End If
122
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
End Sub
Sub BindData()
Dim myConnection As System.Data.SqlClient.SqlConnection
Dim myCommand As System.Data.SqlClient.SqlCommand
Dim sqlString As String
' Get Records From Database
myConnection = New System.Data.SqlClient.SqlConnection _
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
sqlString = "Select * from Products Order By " & sortfield
myCommand = New System.Data.SqlClient.SqlCommand _
(sqlString, myConnection)
myConnection.Open()
myDataGrid.DataSource = myCommand.ExecuteReader()
myDataGrid.DataBind()
myConnection.Close()
End Sub
26.
Private Sub myDataGrid_SortCommand(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs)
Handles myDataGrid.SortCommand
27.
sortField = e.SortExpression
28.
BindData()
29.
End Sub
30. End Class
Strona listingu SqlDataGridSort.aspx formuje element DataGrid z tytułami kolumn w postaci
linków. Przy klikniejciu na odpowiedni link dane będą posortowane po tej kolumnie. Każde
klikniejcie powoduje zdarzenie SortCommand. Procedura obrabiania zdarzenia
myDataGrid_SortCommand() zawiera linie 27 kodu która przypisuje zmiennej sortFild
(zadeklarowanej w linii 5) tekstową nazwę kolumny. Ta nazwa będzie przekazana przez
parametr zdarzenia e.SortExpression. Zmienna sortFild wykorzysta się w metodzie
BindData() (linia 18) dla realizacji polecenia SQL obiektu DataReader.
W przypadkach, kiedy kolumny nie są wygenerowane automatyczne (AutoGenerateColumns
= false) dla sortowania oddzielnych kolumn może być wykorzystana właściwość
SortExpression klasy BoundColumn. W ten sposób można wyznaczyć które z kolumn mogą
być posortowane oraz które nie. Ten przykład jest pokazany w listingu
SqlDataGridSotrCustom.aspx.
Dla definicji kolumn w kontrolce DataGrid zostali
wykorzystane obiekty klas BoundColumn w liniach 12-17. Procedura obrabiania zdarzeń
SortCommand jest wyznaczona w linii 9.
Listing SqlDataGridSotrCustom.aspx.
1. <%@ Page CodeBehind="SqlDataGridSortCustom.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGridSortCustom" %>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:DataGrid id="myDataGrid" AutoGenerateColumns="False"
9.
AllowSorting="True" onSortCommand="SortGrid"
10.
cellpadding="3" Runat="Server">
11.
<Columns>
12.
<asp:BoundColumn HeaderText="Product ID"
13.
DataField="ProductID" />
123
14.
<asp:BoundColumn HeaderText="Product Name"
15.
DataField="ProductName" SortExpression="ProductName" />
16.
<asp:BoundColumn HeaderText="Price" DataField="UnitPrice"
17.
DataFormatString="{0:c}" SortExpression="UnitPrice" />
18.
</Columns>
19.
</asp:DataGrid>
20.
</form>
21. </body>
22. </HTML>
Kod klasy programowej jest pokazany w SqlDataGridSotrCustom.aspx.vb.listingu
Listing SqlDataGridSotrCustom.aspx.vb.
1. Public Class SqlDataGridSortCustom
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Dim sortField As String = "ProductID"
6.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
7.
System.EventArgs) Handles MyBase.Load
8.
If Not IsPostBack Then
9.
BindData()
10.
End If
11.
End Sub
12.
Sub BindData()
13.
Dim myConnection As System.Data.SqlClient.SqlConnection
14.
Dim myCommand As System.Data.SqlClient.SqlCommand
15.
Dim sqlString As String
16.
myConnection = New _
17.
System.Data.SqlClient.SqlConnection("Server=Localhost;
18.
uid=sa;pwd=;Database=Northwind")
19.
sqlString = "Select * from Products Order By " & sortfield
20.
myCommand = New System.Data.SqlClient.SqlCommand _
21.
(sqlString, myConnection)
22.
myConnection.Open()
23.
myDataGrid.DataSource = myCommand.ExecuteReader()
24.
myDataGrid.DataBind()
25.
myConnection.Close()
26.
End Sub
27.
28.
29.
30.
31.
32.
Sub SortGrid(ByVal s As Object, ByVal e As
DataGridSortCommandEventArgs)
sortField = e.SortExpression
BindData()
End Sub
End Class
Podział na strony wyświetlanych danych w obiekcie DataGrid
Kontrolka DataGrid ma możliwość podzielenia na strony wyświetlanych danych, jeśli nie
zmieszczą się na jednej. Kiedy ustawiony jest podział na strony, wynik przetwarzania jest
rozdzielany na zadeklarowaną liczbę stron, a użytkownik może przechodzić pomiędzy
stronami za pomocą przycisków. Numer strony, która ma być wyświetlana aktualnie, jest
określony przez wartość atrybutu CurrenPageIndex obiektu DataGrid. Kiedy użytkownik
kliknie przycisk, aby przejść do kolejnej strony, cały zestaw danych jest tworzony ponownie i
proces podziału na strony rozpoczyna się kolejny raz. Podział na strony uaktywniany jest
przez nadania atrybutowi AllowPaging = True, oraz określenie za pomocą atrybutu PageSize
124
liczbę rekordów, które maja być wyświetlone na jednej stronie. Na stronie właściwości
DataGrid Paging Properties w VSNET można określić styl przycisków do przechodzenia
pomiędzy stronami. Tu można zdefiniować ilość wyświetlanych rekordów na stronie,
zadeklarować wyświetlanie nazw przycisków lub numerów stron.
Dla obsługiwania procesów przejścia pomiędzy stronami musi być opracowana metoda
obrabiania zdarzenia związana z uruchamianiem przycisków przejścia. Istnieją ograniczenia
do wykorzystania wbudowanych możliwości DataGrid stosowne przeglądania po stronach:
 Nie jest możliwym wykorzystania tej możliwości przy wiązaniu z obiektami klasy
DataReader. Te ograniczenie wypływa z konieczności obecności wszystkich
odwzorowanych rekordów w pamięci podręczną, a obiekt DataReader ma tylko jeden
aktualny rekord.
 Nie można ograniczyć ilość stron w pamięci podręcznej ViewState – wszystkie rekordy
muszą być uaktualnione.
W listingu SqlDataGridPaging.aspx jest pokazany przykład wykorzystania DataGrid dla
podziału na strony.
Listing SqlDataGridPaging.aspx.
1. <%@ Page CodeBehind="SqlDataGridPaging.aspx.vb" Language="vb"
AutoEventWireup="false" Inherits="PrezentacjaDanych.SqlDataGridPaging" %>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
7.
<form Runat="Server">
8.
<asp:DataGrid id="myDataGrid" AutoGenerateColumns="False"
9.
AllowPaging="True" cellpadding="3" Runat="Server">
10.
<Columns>
11.
<asp:BoundColumn DataField="ProductID"
12.
HeaderText="ProductID">
13.
</asp:BoundColumn>
14.
<asp:BoundColumn DataField="ProductName"
15.
HeaderText="Product Name">
16.
</asp:BoundColumn>
17.
<asp:BoundColumn DataField="UnitPrice"
18.
HeaderText="Price" DataFormatString="{0:c}">
19.
</asp:BoundColumn>
20.
</Columns>
21.
</asp:DataGrid>
22.
</form>
23. </body>
24. </HTML>
Kod klasy programowej jest pokazany w listingu SqlDataGridPaging.aspx.vb.
Listing SqlDataGridPaging.aspx.vb.
1. Public Class SqlDataGridPaging
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
125
6.
If Not IsPostBack Then
7.
BindData()
8.
End If
9. End Sub
10.
Sub BindData()
11.
Dim myConnection As System.Data.SqlClient.SqlConnection
12.
Dim myAdapter As System.Data.SqlClient.SqlDataAdapter
13.
Dim sqlString As String
14.
Dim myDataSet As DataSet
15.
' Get Records From Database
16.
myConnection = New _
17.
System.Data.SqlClient.SqlConnection _
18.
("Server=Localhost;uid=sa;pwd=;Database=Northwind")
19.
sqlString = "Select * from Products"
20.
myAdapter = New System.Data.SqlClient.SqlDataAdapter _
21.
(sqlString, myConnection)
22.
myDataSet = New DataSet
23.
myAdapter.Fill(myDataSet, "Products")
24.
myDataGrid.DataSource = myDataSet
25.
myDataGrid.DataBind()
26.
End Sub
27.
Private Sub myDataGrid_PageIndexChanged(ByVal source As Object,
ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs)
Handles myDataGrid.PageIndexChanged
28.
myDataGrid.CurrentPageIndex = e.NewPageIndex
29.
BindData()
30.
End Sub
31.
End Class
Procedura obrabiania zdarzenia PageIndexChanged jest wyznaczona w liniach 27-30 kodu. Ta
procedura modyfikuje właściwość CurrentPageIndex kontrolki DataGrid oraz realizuje
ponowne wiązanie ze źródłem danych (obiektem DataSet).
Wyżej było omówione, że wykorzystanie wewnętrznych możliwości DataGrid dla podziału
na strony napotyka się na ograniczenia związane z koniecznością obecności wszystkich
rekordów w pamięci podręcznej (ViewState). To oznaczy się, że nawet w przypadku, kiedy
użytkownik potrzebuje tylko jedną stronę obiekt DataGrid musi zawierać rekordy wszystkich
stron w pamięci podręcznej. W tym przypadku dla oszczędzenia resursów RAM można
stworzyć własne oprogramowanie dla sterowania procesem podziału na strony. Ideą może być
wykorzystanie polecenia SQL o następnej postaci:
Select TOP 5 * From Products
Where ProductID >6
Order By ProductID
Te polecenie wywoła 5 rekordów z tabeli bazy danych z indeksem >6. Dla wywołania
oddzielnych bloków rekordów może być za każdym razem wykorzystane podobne polecenie.
W listingu SqlDataGridPageCustom.aspx jest pokazany przykład wykorzystania sterowaniem
podziału na strony przez program użytkownika.
Listing SqlDataGridPageCustom.aspx .
1. <%@ Page CodeBehind="SqlDataGridPageCustom.aspx.vb" Language="vb"
AutoEventWireup="false"
Inherits="PrezentacjaDanych.SqlDataGridPageCustom"
%>
2. <HTML>
3. <HEAD>
4.
<title>DataGrid</title>
5. </HEAD>
6. <body>
126
7.
Page <%=ViewState( "PageNumber" ) %> of
8.
<%=ViewState( "PageCount" ) %>
9.
<form Runat="Server">
10.
<asp:DataGrid id="myDataGrid" AutoGenerateColumns="False"
11.
cellpadding="3" Runat="Server">
12.
<Columns>
13.
<asp:BoundColumn HeaderText="Product ID"
14.
DataField="ProductID" />
15.
<asp:BoundColumn HeaderText="Product Name"
16.
DataField="ProductName" />
17.
<asp:BoundColumn HeaderText="Price"
18.
DataField="UnitPrice" DataFormatString="{0:c}" />
19.
</Columns>
20.
</asp:DataGrid>
21.
<p>
22.
<asp:LinkButton id="prevButton" Text="<< Previous"
23.
onClick="pageGrid" commandArgument="Previous"
24.
Runat="Server" />
25.
<asp:LinkButton id="nextButton" Text="Next >>"
26.
onClick="pageGrid" commandArgument="Next"
27.
Runat="Server" />
28.
</form>
29.
</P>
30. </body>
31. </HTML>
Kod klasy programowej jest pokazany w listingu SqlDataGridPageCustom.aspx.vb
Listing SqlDataGridPageCustom.aspx.vb.
1. Public Class SqlDataGridPageCustom
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Const PageSize = 10
6. Dim myConnection As System.Data.SqlClient.SqlConnection
7. Dim myAdapter As System.Data.SqlClient.SqlDataAdapter
8. Dim sqlString As String
9. Dim myDataSet As DataSet
10.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
11.
myConnection = New
12.
System.Data.SqlClient.SqlConnection("Server=Localhost;uid=sa;
13.
pwd=;Database=Northwind")
14.
If Not IsPostBack Then
15.
ViewState("PageNumber") = 0
16.
ViewState("PageCount") = getPageCount()
17.
ViewState("startProductID") = -1
18.
ViewState("endProductID") = -1
19.
moveNext()
20.
End If
21.
End Sub
22.
Private Sub Page_PreRender(ByVal sender As Object, ByVal e As
System.EventArgs) Handles MyBase.PreRender
127
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
If ViewState("PageNumber") = ViewState("PageCount") Then
nextButton.Enabled = False
Else
nextButton.Enabled = True
End If
If ViewState("PageNumber") = 1 Then
prevButton.Enabled = False
Else
prevButton.Enabled = True
End If
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
End Sub
Function getPageCount() As Integer
Dim theCount As Double
Dim sqlString As String
Dim myCommand As System.Data.SqlClient.SqlCommand
sqlString = "Select Count(*) theCount From Products"
myCommand = New System.Data.SqlClient.SqlCommand _
(sqlString, myConnection)
myConnection.Open()
theCount = myCommand.ExecuteScalar()
myConnection.Close()
Return Math.Ceiling(theCount / PageSize)
End Function
Sub pageGrid(ByVal s As Object, ByVal e As EventArgs)
If s.CommandArgument = "Next" Then
moveNext()
Else
movePrevious()
End If
End Sub
Sub moveNext()
Dim lastRowIndex As Integer
sqlString = "Select Top " & PageSize.toString() & _
" * from Products " _
& "Where ProductID > " & ViewState("endProductID")._
toString()
myAdapter = New System.Data.SqlClient.SqlDataAdapter _
(sqlString, myConnection)
myDataSet = New DataSet
myAdapter.Fill(myDataSet, "Products")
myDataGrid.DataSource = myDataSet
myDataGrid.DataBind()
ViewState("PageNumber") = ViewState("PageNumber") + 1
ViewState("startProductID") = _
MyDataSet.Tables("Products").Rows(0).Item("ProductID", _
DataRowVersion.Current)
lastRowIndex = myDataSet.Tables("Products").Rows.Count - 1
ViewState("endProductID") = _
MyDataSet.Tables("Products").Rows(lastRowIndex). _
Item("ProductID", DataRowVersion.Current)
End Sub
74.
75.
76.
77.
78.
79.
80.
81.
Sub movePrevious()
Dim myDataView As DataView
Dim lastRowIndex As Integer
sqlString = "Select Top " & PageSize.toString() & _
" * from Products " & " Where ProductID < " & _
ViewState("startProductID").toString() _
& " Order By ProductID DESC"
myAdapter = New System.Data.SqlClient.SqlDataAdapter _
128
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
(sqlString, myConnection)
myDataSet = New DataSet
myAdapter.Fill(myDataSet, "Products")
myDataView = myDataSet.Tables("Products").DefaultView
myDataView.Sort = "ProductID"
myDataGrid.DataSource = myDataView
myDataGrid.DataBind()
lastRowIndex = myDataSet.Tables("Products"). _
Rows.Count - 1
ViewState("PageNumber") = ViewState("PageNumber") - 1
ViewState("startProductID") = _
MyDataSet.Tables("Products").Rows(lastRowIndex). _
Item("ProductID", DataRowVersion.Current)
ViewState("endProductID") = _
MyDataSet.Tables("Products").Rows(0).Item _
("ProductID", DataRowVersion.Current)
End Sub
End Class
Kontrolki walidacyjne ASP.NET
Walidacja jest procesem wielopoziomowym i stanowi zbiór reguł, które nakładają na zbierane
dane. Dane zbierane do walidacji pochodzą z formularzy aplikacji . Walidacja może być
następnych typów:
 Walidacja po stronie klienta;
 Walidacja po stronie serwera.
Kontrolki walidacyjne :
RequiredFieldValidator
Sprawdza, czy do elementu formularza
zostało coś wprowadzone
CompareValidator
Pozwala porównać dane wprowadzone
przez użytkownika z innym elementem (
innym polem formularza lub dowolnej
stalej)
RangeValidator
Sprawdza, czy wartość wprowadzona
przez użytkownika mieści się w podanym
zakresie liczb lub znaków.
RegularExpressionValidator
Sprawdza, czy wpis użytkownika jest
zgodny ze wzorcem zdefiniowanym przez
wyrażenie regularne.
CustomValidator
Sprawdza wpis użytkownika za pomocą
własnej logiki walidacyjnej.
ValidtionSummary
Wyświetla wszystkie komunikaty o
błędach wszystkich kontrolek
walidacyjnych w jednym miejscu na
stronie.
Opracowanie interfejsów graficznych (GUI) za dopomogą
stron wzorcowych.
Strony wzorcowe są narzędziem, pozwalającym tworzyć szablony stron dla wielokrotnego
użytku. Szablon zawiera ogólne wymogi do wyglądu stron, którzy będą dziedziczyły ten
szablon. Dowolna nowa strona może dziedziczyć tylko jeden szablon. Każda strona oprócz
129
komponentów dziedziczonych, może zawierać także i szczegóły interfejsu skojarzone tylko z
tą samą stroną oraz nie dziedziczone od szablonu. Żeby wyznaczyć w szablonie części dla
dziedziczenia oraz części które nie mogą być dziedziczone trzeba wykorzystać kontrolkę
ContentPlaceHolder. Ta kontrolka musi być zdefiniowana w szablonie oraz jest przeznaczona
dla indywidualnych celów każdej strony. Zawartość kontrolki ContentPlaceHolder nie jest
dziedziczona przez inny strony. Ta kontrolka wyznacza przestrzeń interfejsu która nie jest
dziedziczona. Wszystkie inne elementy szablonu poza kontrolek ContentPlaceHolder są
dziedziczone.
Niżej jest rozpatrywany przykład tworzenia i wykorzystania szablonów stron. Na rys. 1 jest
pokazany projekt witryny „szablon”, która zawiera dwie strony wzorcowe typu „master”.
Interfejsy tych stron wzorcowych mogą być dziedziczone.
Rys.1.
Na rys 2. jest pokazane okno dodawania strony wzorcowej do treści projektu witryny(strony
typu „master”). Po stworzeniu strony „master” do jej zawartości automatyczne będzie dodany
obiekt klasy ContentPlaceHolder. Na rys.3 jest pokazany ten obiekt na panelu „Designer”
strony „SiteMaster.master”. Wydruk kodów HTML prezentacji GUI dla tej strony(do
opracowania wzorca) jest pokazany w listingu „Site.master”(do opracowania). Strona
„SiteMaster.master” została dopracowana do innej postaci wzorcowej pokazanej w oknie
projektu na Rys.4. Kody HTML strony wzorca są pokazane w listingu 2. Nowy wzorzec
strony zawiera 3 panele - lewy , centralny, prawy, oraz stopkę. W lewym i prawym będą
prezentowane komponenty ogólne dla wszystkich stron. To są obrazek „Microsoft Windows
XP” w lewym panelu oraz kontrolki „Button” i „Label” w prawym panelu. Przy naciśnięciu
przycisku „Button” w kontrolce „Label” będzie komunikat „Hello World!” na stronie wzorca
oraz na każdej stronie dzidziczonej. Centralny panel oraz stopka mogą zawierać komponenty
GUI wyspecjalizowane dla każdej oddzielnej strony interfejsu. Wzorcowa strona zawiera tu
odpowiednie kontrolki ContentPlaceHolder.
130
Rys. 2.
Rys.3.
Listing1. „Site.master” (do opracowania)
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="Site.master.cs"
Inherits="MasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
&nbsp;&nbsp;&nbsp;<asp:contentplaceholder id="ContentPlaceHolder1"
runat="server">
</asp:contentplaceholder>
</div>
</form>
</body>
</html>
131
Rys.4.
Listing2. SiteMaster.master (po opracowaniu)
<%@ Master Language="C#" AutoEventWireup="true"
CodeFile="SiteMaster.master.cs" Inherits="SiteMaster" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<table width=100% border=2 callspacing=1 cellpadding=1 cellspacing="3">
<tr>
<td valign=top style="height: 199px; width: 150px; text-align:
justify;">
To jest lewy panel ustalonej treści, zawierający obiekty,
które będą dziedziczone
na każdej stronie
<asp:Image ID="Image1" runat="server"
ImageUrl="~/image/winxp.gif"
Style="left: 22px; position: absolute; top: 152px; zindex: 100;" Width="140px" />
&nbsp;
</td>
<td valign=top style="height: 199px; width: 180px;">
<asp:ContentPlaceHolder ID="plhMain" runat=server>
Treść po środku dowolnej strony znajdzie się
tutaj&nbsp;</asp:ContentPlaceHolder>
</td>
<td valign= top align=right style="height: 199px; width: 140px;
text-align: justify;">
To jest prawa strona dla ustalonej treści &nbsp;
132
<asp:Label ID="Label1" runat="server" Text="Label"
Width="149px"></asp:Label>
<asp:Button ID="Button1" runat="server"
Text="Button" OnClick="Button1_Click" /></td>
</tr>
<tr>
<td colspan=3>
<asp:ContentPlaceHolder ID="plhFooter" runat=server>
W tym miejscu znajdzie się treść stopki dowolnej
strony</asp:ContentPlaceHolder>
</td>
</tr>
</table>
</form>
</body>
</html>
Żeby stworzyć stronę, która dziedziczy stronę wzorcową trzeba ustalić właściwość
dziedziczenia „select master page” w oknie dodawania nowego komponentu projektu. To
niemożliwe zrobić, kiedy strona już istnieje. Dlatego wszystkie strony dziedziczone trzeba
dodawać do projektu.
Na rys 5 jest pokazane okno dla dodawania strony
ContentSample.aspx, która będzie dziedziczyć stronę wzorcową SiteMaster.master. Strona ta
została dopracowana nowymi komponentami interfejsu. Na rys.6 jest pokazane okno projektu
strony ContentSample.aspx . Do strony wprowadzono następne zmainy:
 do panelu centralnego dodana kontrolka „Calendar” oraz
 stopka strony zawiera logo Politechniki Koszalińskiej.
W listingu 3 pokazany jest kod HTML strony ContentSample.aspx.
Przy uruchomieniu strony w przeglądarce będą odwzorowane wszystkie komponenty GUI.
Na rys. 7 jest pokazany ostateczny wygląd strony przy uruchomieniu w przeglądarce.
Rys.5.
133
Rys. 6.
Listing3. ContentSample.aspx.
<%@ Page Language="C#" MasterPageFile="~/SiteMaster.master"
AutoEventWireup="true" CodeFile="ContentSample.aspx.cs"
Inherits="ContentSample" Title="Untitled Page" %>
<asp:Content ID="Content1" ContentPlaceHolderID="plhMain" Runat="Server">
To się pojawi w głównym połu zawartości<br />
<asp:Calendar ID="Calendar1" runat="server"></asp:Calendar>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="plhFooter" Runat="Server">
&copy: Politechnika Koszalińska
</asp:Content>
134
Rys.7.
Tworzenie interfejsu graficznego za dopomogą tematów i
skórek.
Wykorzystanie tematów i skórek pozwoli się personalizować witryny internetowe poprzez
ustawienie wyglądu i działania kontrolek w sposób spełniający preferencje projektanta.
Temat to jest zbiór skórek opisujące wygląd, jaki powinny przyjąć kontrolki. Każdy temat w
projekcie witryny musi być rozlokowany w podkatalogu App_Themes . Ten folder może być
dodany do witryny przez opcję „Add ASP.NET Folder” menu kontekstowego pokazanego
na rys. 1
135
Rys.1 Dodanie tematu do witryny.
Do tego folderu można dodać arkusze styli (pliki.css) i pliki skórek (pliki .skin).
Istnieją dwie metody określania tematu .
1. Pierwsza polega na wykorzystaniu
elementu <pages> z pliku Web.Config, np.:
<pages theme=”Green”>
Ta linia kodu XML w pliku konfiguracyjnym wskazuje, że wszystkie strony w danej aplikacji
będą domyślnie korzystały z tematu Green. Każda strona będzie mogła samodzielnie nadpisać
wybór tematu na jeden z dwóch sposobów.
2. Druga metoda wykorzystuje dyrektywę @Page bezpośrednio na stronie, np.:
<%@
Page
Language="C#"
MasterPageFile="~/Site.master"
AutoEventWireup="true"
CodeFile="skindemo.aspx.cs"
Inherits="skindemo"
Title="Untitled Page" Theme="Red"%>
Ten kod jest wykorzystany przez stronę do przesłonięcia ustawień z pliku Web.Config i
wymusza zastosowanie tematu „Red” dla tej strony.
Można także programowo ustawić bieżący temat dla strony, np.:
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
Theme = "red";
}
Przykład demonstracji wykorzystania tematów oraz skórek jest pokazany w witrynie
C:\Inetpub\wwwroot\ThemesSkins. Do witryny została dodana strona wzorcowa Site.master .
Wygląd strony w panelu designer jest pokazany na rys. 2.
136
Rys.2
Kod tej strony pokazany w listingu.
Listing 1. (Kod strony Site.master)
1. <%@ Master Language="C#" AutoEventWireup="true"
CodeFile="Site.master.cs" Inherits="Site" %>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml" >
4. <head runat="server">
5. <title>Untitled Page</title>
6. </head>
7. <body>
8. <form id="form1" runat="server">
9. <div>
10.
<img src="image/winxp.gif" />&nbsp;
11.
</div>
12.
<table>
13.
<tr>
14.
<td style="width: 906px">
15.
<asp:contentplaceholder id="ContentPlaceHolder1"
runat="server">
16.
</asp:contentplaceholder>
17.
</td>
18.
</tr>
19.
</table>
20.
</form>
21.
</body>
22.
</html>
W znacznikach
<asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
</asp:contentplaceholder>
137
jest wyznaczony element ContentPlaceholder który zawiera przestrzeń dla tworzenia
interfejsu na stronach witryny. Interfejs strony będzie rozlokowany tylko w kontrolce
Content. W tej przestrzeni będą nie dziedziczone elementy interfejsu. Strona default.aspx
dziedziczy stronę wzorcową oraz zawiera dodatkowe elementy interfejsu w obszarze
Content. W pliku Web.Config są ustalone wymagania dla wszystkich stron, którzy muszą
mieć wygląd wyznaczony zgodnie z tematem Green. Kod pliku Web.Config jest pokazany w
listingu 2.
Listing 2. (Kod pliku Web.Config)
1. <?xml version="1.0"?>
2.
<configuration>
3.
<appSettings/>
4.
<connectionStrings/>
5.
<system.web>
6.
<pages theme="Green" />
7.
<compilation debug="false" />
8.
<authentication mode="Windows" />
9.
</system.web>
10.
</configuration>
W linii 6 jest ustalony temat „Green” dla wszystkich stron witryny.
Kod default.aspx strony jest pokazany w listingu 3.
Listing 3. (kod strony default.aspx)
1. <%@ Page Language="C#" MasterPageFile="~/Site.master"
AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"
Title="Untitled Page" %>
2. <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1"
Runat="Server">
3. &nbsp;<asp:Button ID="Button1" runat="server" Height="34px"
Text="Button" Width="96px" /><br />
4. <br />
5. <asp:Label ID="Label1" runat="server" Font-Size="Large" Height="31px"
Text="Tworzenie interfejsu Użytkownika"
Width="254px"></asp:Label><br />
6. </asp:Content>
Wygląd strony przy uruchomieniu jest pokazany na rys. 3. Na rys 3 są zielone tło, czcionka
normal po domyśleniu, ustalone w pliku green.css tematu Green.
138
Rys.3
Temat tej strony może być zmieniony w sposób programowy . W listingu 4 jest pokazany
kod klasy pośredniej dla strony default.aspx.cs.
Listing 4. (strona default.aspx.cs )
7. using
8. using
9. using
10.
11.
12.
13.
14.
15.
16.
System;
System.Data;
System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
17.
18.
19.
20.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
21.
22.
23.
24.
25.
26.
}
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
Theme = "red";
}
}
27.
W liniach 22-26 jest wykorzystana metoda OnPreInit() dla przesłonięcia tematu Green,
wyznaczonego w pliku Web.Config przez temat Red, omowiony w pliku red.css. Nowy
wygląd strony jest pokazany na rys.4. (na rys. są czerwone tło, czcionka Italic )
139
Rys.4
Dla realizacji dynamicznego sterowania wyglądem kontrolek trzeba wykorzystać pliki skórek
odpowiednich tematów. W listingu 5 jest pokazany plik controls.skin tematu Green .
Listing 5. (plik controls.skin tematu Green)
1. <%-- domyślny przycisk--%>
2. <asp:Button runat="server" ForeColor="Green" BackColor="White"/>
3. <%-- przycisk pojawia się po wyborze identyfikatora skórki 'Flat'-%>
4. <asp:Button runat="server" ForeColor="Black" BackColor="Blue"
BorderStyle="None" SkinID="Flat" />
5. <%-- przycisk pojawia się po wyborze identyfikatora skórki
'Inverse'--%>
6. <asp:Button runat="server" ForeColor="White" BackColor="Green"
SkinID="Inverse" />
Kod w listingu 5 zawiera częściową definicję kontrolek. Zwłaszcza są parametry
runat="server", ale są nieobecne identyfikatory ID kontrolek. Definicje kontrolek w tym pliku
są przeznaczone dla ustalenia właściwości interfejsu użytkownika i wyświetlania. To oznacza,
że można ustawić style wierszy
parzystych i nieparzystych kontrolki DataGrid, ale
nie można w tym pliku wyznaczyć źródła danych. W pliku Listing 5. są wyznaczone wyglądy
trzech typów kontrolek „Button” . W linii 2 jest ustalone wymogi do wszystkich kontrolek
„Button” po domyśleniu. Dla tych kontrolek nie trzeba wyznaczać parametry SkinID. W
liniach 4 -6 są wyznaczone inne wyglądy kontrolek „Button” mające ustalone parametry
SkinID. Przy stworzeniu interfejsu stron .asp trzeba odwołać się do parametrów SkinID w
kontrolkach mające inny wygląd inny czym wygląd po domyśleniu. Przykład strony
skindemo.aspx jest pokazany w listingu 6.
Listing 6 (strona skindemo.aspx.)
1. <%@ Page Language="C#" MasterPageFile="~/Site.master"
AutoEventWireup="true" CodeFile="skindemo.aspx.cs"
Inherits="skindemo" Title="Untitled Page" Theme="green"%>
2. <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1"
Runat="Server">
140
3.
4.
5.
6.
7.
8.
9.
<asp:Button ID="defaultButton" runat="server" Text="To jest
domyślny przycisk" Width="291px" /><br />
<br />
<asp:Button ID="flatButton" runat="server" Text="To jest płaski
przycisk" Width="290px" SkinID= "Flat" />
<br />
<br />
<asp:Button ID="inverseButton" runat="server" Text="To jest
odwroconyi przycisk" Width="290px" SkinID="Inverse" />
</asp:Content>
W przestrzeni Content zostali zdefiniowane trzy kontrolki „Button” którzy odwalają się do
odpowiednich skórek. Wygląd strony jest pokazany na rys. 5.
Rys.5
Opracowanie mechanizmów nawigacji witryn WWW
W przypadkach komplikowanych projektów witryn WWW zawierających wiele stron
powstaje pytanie stworzenia mechanizmów nawigacji, pozwalających podpowiedzieć
użytkownikowi gdzie on się znajduje oraz w jaki sposób można zrealizować przejście do
innej strony. Zestaw kontrolek i klas ASP.NET zawiera zasoby sterowania nawigacją w
witrynie. W większości przypadków trzeba rozmieścić tę kontrolki na każdej stronie, dlatego
lepiej wykorzystać stronę wzorcową dla tworzenia stron. Głównym elementem nawigacji jest
plik XML , zawierający hierarchiczną bazę danych XML węzłów witryny. Ten plik może
być dodany do projektu przez okno „Add New Item” . Po domyśleniu plik nawigacji ma
nazwę „Web.sitemap”. Szkielet tego pliku przy stworzeniu jest pokazany w listingu 1.
Listing 1. Web.sitemap (szkielet pliku nawigacji ).
<?xml version="1.0" encoding="utf-8" ?>
141
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="" title="" description="">
<siteMapNode url="" title="" description="" />
<siteMapNode url="" title="" description="" />
</siteMapNode>
</siteMap>
Realna mapa witryny musi zawierać uzupełnione definicje znaczników XML <siteMapNode>
oraz druga linia kodu w listingu 1 odwoła się do pliku tych definicji. Atrybut „URL” w linii 3
musi zawierać URL strony witryny odpowiadającej węzłowi, atrybut „title” definiuje tekst,
który jest używany jako łącze, natomiast atrybut „description” będzie używany jako
podpowiedź. Realny plik Web.sitemap musi zawierać uzupełnioną strukturę węzłów. Dla
demonstracji możliwości nawigacji jest stworzony projekt witryny. Projekt SiteNavigation
zawiera przykłady stron witryny oraz plik Web.sitemap są pokazane na rys.1.
Rys.1
Plik Web.sitemap w listingu 1 został zastąpiony plikiem mapy witryny mający ostateczny
wygląd w listingu 2.
Listing 2. Web.sitemap (mapa stron witryny)
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode title="Witamy" description="Witamy" url="~/welcome.aspx">
<siteMapNode title="Pisanie" description="Pisanie"
url="~/Writing.aspx">
<siteMapNode title="Książki" description="Książki"
url="~/Books.aspx">
<siteMapNode title="Książki w druku"
description="Książki w druku"
url="~/BooksInPrint.aspx" />
<siteMapNode title="Książki niedostępne"
description="Książki niedostępne"
url="~/OutOfPrintBooks.aspx" />
</siteMapNode>
<siteMapNode title="Artykuły" description="Artykuły"
142
url="~/Articles.aspx" />
</siteMapNode>
<siteMapNode title="Programowanie"
description="Programowanie"
url="~/Programming.aspx">
<siteMapNode title="Prgramowanie On-Site"
description="Programowanie On-site"
url="~/OnSiteProgramming.aspx" />
<siteMapNode title="Programowanie Off-Site"
description="Programowanie Off-site"
url="~/OffSiteProgramming.aspx" />
</siteMapNode>
<siteMapNode title="Szkolenia"
description="Szkolenia na miejscu "
url="~/Training.aspx">
<siteMapNode title="Szkolenia w zakresie C#"
description="Szkolenia w zakresie C#"
url="~/TrainCSharp.aspx" />
<siteMapNode title="Szkolenia w zakresie VB"
description="Szkolenia w zakresie VB"
url="~/TrainVBNet.aspx" />
<siteMapNode title="Szkolenia w zakresie ASP.NET"
description="Szkolenia w zakresie ASP.NET"
url="~/TrainASPNET.aspx" />
<siteMapNode title="Szkolenia w zakresie technologii Windows Forms"
description="Szkolenia w zakresie technologii Windows Forms"
url="~/TrainWinForms.aspx" />
</siteMapNode>
<siteMapNode title="Doradztwo"
description="Doradztwo"
url="~/Consulting.aspx">
<siteMapNode title="Analiza aplikacji"
description="Analiza aplikacji"
url="~/ApplicationAnalysis.aspx" />
<siteMapNode title="Projektowanie aplikacji"
description="Projektowanie aplikacji"
url="~/ApplicationDesign.aspx" />
<siteMapNode title="Mentoring"
description="Mentoring zespołu"
url="~/Mentoring.aspx" />
</siteMapNode>
</siteMapNode>
</siteMap>
Plik mapy witryny posiada pojedynczy element <sitemap>, który definiuje przestrzeń nazw
XML. Wewnątrz elementu siteMap został zagnieżdżony jeden element <SiteMapNode>,
odpowiadający stronie „Welcome” witryny. Ten pierwszy element zawiera pewną liczbę
elementów potomnych <siteMapNode>.
Wszystkie strony dziedziczą od strony wzorcową Master.page. Do tej strony zostali dodane
kontrolki którzy będą dziedziczone przez inne strony. To są kontrolki: SiteMapDataSource,
TreeView, SiteMapPath oraz ContentPlaceHolder. Wydruk strony projektu wzorca jest
pokazany w listingu 3.
Listing 3. Master.Page.
<%@ Master Language="C#" AutoEventWireup="true"
CodeFile="MasterPage.master.cs" Inherits="MasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
143
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />
<asp:Table ID="Table1" runat="server" Width="100%" Height="233px"
BorderWidth="3px" >
<asp:TableRow runat="server">
<asp:TableCell runat="server" BorderWidth="3px">
<asp:TreeView ID="TreeView1" runat="server"
DataSourceID="SiteMapDataSource1" />
</asp:TableCell>
<asp:TableCell VerticalAlign="Top" runat="server">
<asp:SiteMapPath ID="SiteMapPath1" runat="server" />
<br /><br />
<asp:contentplaceholder id="ContentPlaceHolder1"
runat="server">
</asp:contentplaceholder>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
</div>
</form>
</body>
</html>
Wszystkie kontrolki zostali rozmieśczone w jednym wierszu tablicy oraz w dwoch kolumnach
tego wierszu. W lewej kolumnie jest rozmiejśczony element TreeView oraz w prawej
kolumnie są kontrolki SiteMapDataSource i ContentplaceHolder. Żrodlem danych kontrolki
TreeView jest wyznaczona kontrolka SiteMapDataSource. Kontrolka SiteMapDataSource po
domyśleniu jest skojarzona z plikiem mapy Web.Sitemap, dlatego każda strona witryny
będzie zawierać hierarchiczną mapę witryny z lewej części prezentowaną elementem
TreeView oraz nawigacyjny wizualny element typu „jesteś tutaj” z prawej części,
prezentowany przez kontrolkę SiteMapDataSource. Oprócz tego, w prawej części każdej
strony mogą być rozmieszczone specyficzne zawartości każdej strony w kontrolce Content.
Kontrolka Content jest stworzona w sposób automatyczny i odwoła się do kontrolki
ContentPlaceHolder wzorca MasterPage.master. Przykład strony witryny jest pokazany na
rys. 1
144
\
Rys.1
Tworzenie serwisów WWW
Serwis WWW (usługa WWW, usługa sieciowa, usługa Web) to jest komponent programowy,
niezależny od platformy i implementacji, rozmieszczony na serwerze WWW, dostarczający
metody które mogą być wywołane przez sieć za pomocą protokołów standardowych HTTP
oraz XML. Usługa WWW może być opisana za pomocą języka opisu usług, opublikowana w
rejestrze (katalogu) usług, oraz może być wyszukana za pomocą standardowego mechanizmu.
Usługa WWW może być wywołana za pomocą interfejsu API w sieci. Główną zaletą
serwisów WWW jest możliwość zjednoczenia możliwości funkcjonalnych dla
niekompatybilnych systemów informatycznych np. J2EE oraz .NET, którzy mogą być
rozproszone na różnych hostach sieci.
145
Dla uruchomienia serwisu WWW oraz przekazania parametrów wejściowych trzeba
wykorzystać odpowiednie standardowe protokoły HTTP. Serwis WWW to jest czarna
skrzynia zawierająca komplikowane możliwości funkcjonalne z prostym interfejsem. Dostęp
do serwisów WWW może być zrealizowany przez protokoły standardowe WWW.
W
środowisku .NET Framework Web Serwis może być zrealizowany w postaci obiektu COM,
który oprócz binarnych ma dodatkowe interfejsy obsługiwania zapytań HTTP.
Standardowymi protokółami WWW są :
 HTTP-Get – protokół umożliwiający klientom komunikację z serwerami przy użyciu
protokołu HTTP. Klient przesyła na serwer żądania HTTP dotyczące zasobu o
konkretnym adresie URL, a serwer w odpowiedzi zwraca odpowiedni kod HTML.
Wszelkie parametry konieczne do prawidłowego obsłużenia żądania są dołączone do
adresu URL jako łańcuch zapytania w postaci par nazwa-wartość. Serwisy sieci WWW
mogą wykorzystywać żądania HTTP-Get i łańcuch zapytania do przesyłania poleceń i
zwracania wyników, zamiast komunikatów zapisanych w języku XML. Informacje
przesyłane w ten sposób stanowią część adresu URL. Protokół ten ma ograniczone
możliwości, gdyż umożliwia przesyłanie wyłącznie par nazwa-wartość.
 HTTP-Post – protokół ten przypomina HTTP-Get, lecz różni się od niego tym, iż
informacje nie są przekazywane w formie łańcucha zapytania, lecz zapisywane w
nagłówkach żądania HTTP. Gdy klient przesyła żądanie przy użyciu tego protokołu,
generuje on żądanie HTTP zawierające dodatkowe informacje zapisane w formie par
nazwa-wartość w nagłówkach HTTP. Serwer odbierając takie żądanie musi je odczytać z
nagłówków oraz przetworzyć, aby określić nazwy parametrów oraz ich wartości. Protokół
ten jest najczęściej wykorzystywany przy przesyłaniu formularzy HTML. Możliwości
tego protokołu, podobnie jak poprzedniego, ograniczają się jedynie do przesyłania par
nazwa-wartość.
 SOAP (Simple Object Access Protocol) – prosty protokół dostępu do obiektów – w
odróżnieniu od poprzednich protokołów przesyła informacje w formie XML. Oznacza to ,
że przy użyciu tego protokołu można przesyłać nie tylko proste pary nazwa-wartość, lecz
także znacznie bardziej skomplikowane obiekty, takie jak złożone typy danych, klasy
obiekty itp. Protokół SOAP jest nadbudową nad HTTP, to oznaczy, że wykorzystuje
HTTP, jednak jego działanie nie ogranicza się do modelu żądanie-odpowiedż. Ponieważ
protokół ten bazuje się na języku XML, można go wykorzystywać do przesyłania
informacji za pośrednictwem WWW bez ograniczeń.
Demonstracja działania serwisów Web D:\2310B\Media\2310B_13A001.htm
Na rys. 44 jest pokazany ogólny schemat poszukiwania i rejestracji usługi WWW zgodnie ze
standardem UDDI(Universal Description Discovery and Integration). Ten schemat zawiera
następne czynności:
1. Publikacja w katalogu UDDI (w formacie XML) URL deskryptora .disko serwisu
WWW. Deskryptor .disko to jest plik w formacie XML, który zawiera odwołania
URL do resursów serwisu WWW.
2. Znajdowanie klientem w katalogu UDDI przez przeglądarkę UDDI URL deskryptora
.disko serwisu WWW.
3. Odczytanie deskryptora .disko , który zawiera lokalizacje usług WWW w postaci
URL.
4. Odczytanie pliku .wsdl do generacji klasy proxy usługi WWW.
5. Generacja klasy proxy oraz ustalenie połączenie z klasą usługi WWW.
6. Wywołanie usługi WWW przez klasę proxy, przekazanie ją parametrów oraz
otrzymanie z powrotem rezultatów.
146
Rys.44
Te kroki są niezbędne, kiedy projektant nie wie, gdzie jest lokalizowana usługa WWW. W
przypadku, kiedy użytkownik dokładnie wie URL serwisu WWW można bezpośrednio
odczytać deskryptor .disko tej usługi bez wykorzystania protokołu UDDI.
WWW serwis może być stworzony w środowisku VS .NET w sposób podobny do stworzenia
projektu aplikacji ASP.NET. W tym przypadku przy wywołaniu trybu „New Project” w
VS.NET w oknie „Templates” zamiast ikony „ASP.NET Web Applikcation” trzeba wybrać
„ASP.NET Web Service”. Środowisko VS.NET tworzy się komponenty projektu do WWW
serwisu. Projekt serwisu WWW zawiera pliki
Web.config, Global.asax oraz plik
„Serwis_Name.asmx” z kodem serwisu. Kod pliku .asmx zawiera wszystkie niezbędne
definicji do stworzenia klasy WWW serwisu. Sam serwis jest zawarty w pliku .dll katalogu
bin projektu. Przykład kodu WWW serwisu jest pokazany w listingu Service1.asmx.
(Projekty ClientWebService1, WebService1, WebServiceCalculator)
Listing Service1.asmx.
1. Imports System.Web.Services
2. <System.Web.Services.WebService(Namespace :=
"http://tempuri.org/WebService1/Service1")> _
3. Public Class Service1
4. Inherits System.Web.Services.WebService
5. #Region " Web Services Designer Generated Code "
6. #End Region
7. ' WEB SERVICE EXAMPLE
8. ' The HelloWorld() example service returns the string Hello World.
9. ' To build, uncomment the following lines then save and build the
project.
10.
' To test this web service, ensure that the .asmx file is the
start page
11.
' and press F5.
12.
'
13.
'<WebMethod()> _
14.
'Public Function HelloWorld() As String
15.
'
Return "Hello World"
147
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
'End Function
<WebMethod()> _
Public Function TestString(ByVal x As String) As String
TestString = _
"TestString received the following as input: " & x
End Function
<WebMethod()> _
Public Function TestMath(ByVal y As Integer, ByVal z As _
Integer) As Integer
TestMath = y + z
End Function
27. End Class
Wszystkie metody serwisów WWW mają atrybut <WebMethod()>. W liniach 7-16
są
pokazane instrukcji dla stworzenia metod serwisów WWW na przykładzie znanej metody
HelloWorld(). W liniach 18-21 jest stworzona przykładowa metoda serwisu WWW
TestString(ByVal x As String),w postaci funkcji, która dodaje do argumentu
wejściowego x następny tekst: "TestString received the following as input: " . Ten
tekst razem ze zmiennej wejściowej x będzie z powrotem odesłany do strony, która wywołała
ten serwis. W liniach 23-26 jest zdefiniowana metoda WWW serwisu TestMath(ByVal y As
Integer, ByVal z As Integer), która realizuje operację sumy argumentów .
Serwis można protestować bez aplikacji wywołującej ten serwis. Dlatego trzeba uruchomić
plik .asmx w przeglądarce. Będą wyświetlane linki do deskryptora serwisu w postaci pliku
WSDL (Web Services Description Language) oraz do metod serwisu. Plik WSDL definiuje
interfejs usługi sieciowej, szczegóły wiązania (protokół sieciowy, wymagania związane z
kodowaniem danych) oraz jej umiejscowienie w sieci. Ten plik jest potrzebny dla stworzenia
klasy proxy usługi WWW na stronie klienta. Testować usługę WWW można i bez strony
klienta. W tym przypadku przy testowaniu serwisu WWW jest wykorzystany protokół HTTP
oraz nie jest potrzebny protokół SOAP. Klikniejcie linka spowoduje wywołanie odpowiedniej
metody usługi WWW i przekazanie do tej metody wprowadzonego lub wprowadzonych
parametrów. Jeśli wewnątrz metody zostanie wygenerowany błąd, to komunikat o błędzie
zostanie wyświetlony na stronie. Jeśli metoda zostanie wykonana bez błędu, na stronie
zostanie wyświetlona wartość zwrócona przez tę metodę w postaci znaczników XML.
Kod strony klienta jest pokazany w listingu WebForm1.aspx.vb.
Listing WebForm1.aspx.vb
1. Public Class WebForm1
2. Inherits System.Web.UI.Page
3. #Region " Web Form Designer Generated Code "
4. #End Region
5. Private Sub ButtonA_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles ButtonA.Click
6.
Dim objService As New ClientWebService1.localhost.Service1
7.
Me.Label1.Text = objService.TestString(Me.TextBox1.Text)
8. End Sub
9. Private Sub ButtonB_Click1(ByVal sender As Object, ByVal e As
System.EventArgs) Handles ButtonB.Click
10.
Dim objService As New ClientWebService1.localhost.Service1
11.
If (Me.TextBox2.Text <> "") And (Me.TextBox3.Text <> "") Then
12.
Me.Labelrezult.Text = "Rezultat = " + _
13.
objService.TestMath(CType(Me.TextBox2.Text, Integer), _
14.
CType(Me.TextBox3.Text, Integer)).ToString
15.
End If
16.
End Sub
17.
End Class
148
Dodawanie klasy proxy do strony klienta może być zrealizowane w projekcie VS.NET przez
opcję menu „Add Web Reference”. Na rys.45 jest pokazana zawartość metod klasy proxy
serwisu WWW „Service1” w Object Browser VS.NET. Ta klasa oprócz podstawowych
metod TestMath() oraz TestString() zawiera metody typu BeginXXX oraz EndXXX. Te
metody mogą być wykorzystane do uruchomienia serwisu WWW w trybie asynchronicznym.
Rys.45
Tryb synchroniczny został wykorzystany w poprzednim listingu. Wywołanie synchroniczne
musi powstrzymać realizację aplikacji klienta na termin oczekiwania realizacji serwisu
WWW.
W trybie asynchronicznym aplikacja może kontynuować pracę. Przykład
wykorzystania serwisu WWW „TestMath” w trybie asynchronicznym dla poprzedniego
przykładu jest pokazany w listingu WebForm1.aspx.vb (tryb asynchroniczny).
Listing WebForm1.aspx.vb(tryb asynchroniczny).
1.
2.
3.
4.
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
#End Region
5. Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
6. End Sub
7. Private Sub ButtonA_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles ButtonA.Click
8.
Dim objService As New ClientWebService1.localhost.Service1
9.
Me.Label1.Text = objService.TestString(Me.TextBox1.Text)
10.
End Sub
11.
Private Sub ButtonB_Click1(ByVal sender As Object, ByVal e As
System.EventArgs) Handles ButtonB.Click
12.
Dim objService As New ClientWebService1.localhost.Service1
13.
'asynchroniczny tryb WWW serwisu(1 linia):
14.
Dim ar As IAsyncResult
15.
'-----------koniec kodu asynchronicznego-------16.
If (Me.TextBox2.Text <> "") And (Me.TextBox3.Text <> "")_
17.
Then
18.
'asynchroniczny tryb WWW serwisu(3 linii):
19.
ar = objService.BeginTestMath _
20.
(CType(Me.TextBox2.Text, Integer),_
21.
CType(Me.TextBox3.Text,Integer), Nothing, Nothing)
149
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
ar.AsyncWaitHandle.WaitOne()
Me.Labelrezult.Text = "Rezultat = " + _
objService.EndTestMath(ar).ToString
'-----------koniec kodu asynchronicznego-------' synchroniczny tryb WWW serwisu (2linii):
'
Me.Labelrezult.Text = "Rezultat = " +_
'
objService.TestMath(CType(Me.TextBox2.Text, Integer), _
'
CType(Me.TextBox3.Text, Integer)).ToString
'---------koniec kodu synchronicznego---------End If
End Sub
End Class
procedury ButtonA_Click()jest zadeklarowana zmienna obiektowa ar klasy
W linii 14
IAsyncResult. Egzemplarz tej klasy został stworzony w linii 19-21 za dopomogą metody
BeginXXX() klasy proxy „objService”. Metoda BeginXXX() w tym przypadku ma cztery
parametry: pierwszy dwa wyznaczają parametry wejściowe x oraz y dla serwisu WWW,
trzeci parametr wyznacza odwołanie do obiektu AsyncCallback oraz parametr czwartyodwołanie do stworzonej klasy proxy usługi WWW. Parametry trzeci i czwarty nie są w tym
przypadku potrzebne oraz dlatego zostali ustalone wartości Nothing. W linii 22 metoda
WaitOne() ustali się tryb oczekiwania równoległych procesów klientem usługi WWW. W tym
przypadku trzeba czekać zakończenia jednego procesu skojarzonego z metodą TestMath(). W
liniach 23-24 metoda EndTestMath() wraca rezultaty.
Technologia AJAX w ASP.NET.
Technologia AJAX ( Asynchronous JavaScript and XML – asynchroniczny kod JavaScript
oraz XML) łączy w sobie szereg innych technologii :
 HTML(XHTML) i CSS do tworzenia interfejsu użytkownika
 Model DOM (Document Object Model) brousera klienta do obsługi elementów
dynamicznych i interakcji
 Obiektu XMLHttpRequest (XHR) do asynchronicznej wymiany danych.
Główna idea technologii AJAX polega w tym, że oddzielne elementy interfejsu klienta w
przeglądarce internetowej mogą być uaktualnieni przez wykonanie asynchronicznych
wywołań, zapytań do innych stron czy serwisów WWW bez konieczności obrazowania i
uaktualnienia innych elementów interfejsu. Innymi słowy jest możliwe otrzymanie danych na
stronie klienta bez konieczności powtórnego przeładowania całej strony. Dla zrozumienia
zasad technologii AJAX trzeba rozpatrzyć następne poziomy realizacji tej technologii:
 Wykorzystanie obiektu XMLHttpRequest dla asynchronicznej wymiany danych.
 Wykorzystanie API Script Callback (zwrotne wywołania stron).
 Wykorzystanie kontrolek biblioteki MS ASP.NET 2.0 AJAX.
1.Wykorzystanie obiektu XMLHttpRequest dla asynchronicznej wymiany danych.
Obiekt XMLHttpRequest jest głównym w technologii AJAX. Po raz pierwszy ten obiekt
został zaimplementowany w IE 5.0. Obiekt XMLHttpRequest powstał na pobieranie stron
WWW z serwera za pomocą metody GET lub wysyłanie do serwera żądania za pomocą
metody POST. Dla uruchomienia metod i wykorzystania właściwości XHR trzeba stworzyć
instancję
obiektu w kodzie JScript klienta. W przypadku, kiedy obiekt jest
zaimplementowany do przeglądarki właściwość window.XMLHttpRequest= true. W innym
przypadku trzeba uruchomić komponent COM Microsoft.XMLHttp. Trzeba zauważyć, że
przy wykorzystaniu bibliotek AJAX oraz narzędzi implementowanych do VS2005 nie będzie
potrzeby bezpośrednio tworzyć ten obiekt w kodach programowych. Wyższe nazwane
programowe środowisko realizuje niezbędne funkcje tego obiektu w sposób przezroczysty dla
programisty. Aczkolwiek rozpatrzenie zasad wykorzystania tego obiektu jest polecane dla
150
lepszego zrozumienia technologii AJAX. W tablice 1 są pokazane metody obiektu
XMLHttpRequest.
Tablica 1.
Metoda (parametry)
Opis
abort()
Anuluje aktualne żądanie HTTP
getAllResponseHeader ()
Pobiera wartości wszystkich nagłówków HTTP
getAllResponseHeader
Pobierany zostanie wyznaczony nagłówek
(header)
open (metod,url,asynch,
Zainicjalizowane zostanie żądanie do serwera. Pierwszy
username,password)
parametr – metoda polecenia: PUT,GET, POST . Drugi
parametr – URL polecenia. Trzeci (opcjonalny) – typ polecenia
(synchroniczny lub asynchroniczny). Czwarty oraz piąty są
opcjonalne , przeznaczone dla chronionych stron.
send (content)
Wysyła żądanie do serwera i odbiera odpowiedź.
setRequestHeader
Ustalenie wartości klucza nagłówka. (Musi być wcześniej
(header,value)
sformowana metoda open().)
W tablice 2 są pokazane właściwości obiektu XMLHttpRequest.
Tablica 2.
Właściwość
Opis
onreadyStatechange
Jest wyznaczona metoda dla obsługiwania zdarzeń przy każdej
zmianie stanu polecenia
readyState
Zwraca stan aktualnego żądania. Są dostępne następne wartości:
0- żądanie nie zostało zainicjalizowane, 1 – trwa lądowanie, 2lądowanie zakończone,
3 – interaktywny, 4 – gotowy
(complete).
responseText
Odpowiedź serwera w wyglądzie tekstu z łańcuchem znaków
responseXML
Odpowiedź serwera w wyglądzie obiektu XML. Ten obiekt
może być weryfikowany oraz parserowany jako komponent
DOM.
status
Kod statusu protokołu HTML, np. 200 – OK.
StatusText
Nazwa kodu statusu HTML w wyglądzie łańcucha tekstu
W listingu HTMLPage.htm (projekt AJAX1) jest pokazany kod strony JScript gdzie jest
wykorzystany obiekt XMLHttpRequest. Strona zawiera przycisk typu button. Naciśniecie
przycisku powoduje uruchomienie na serwerze metody formującej komunikat „Hello World”,
który będzie wyświetlany na stronie bez konieczności przeładowania strony.
Listing HTMLPage.htm
1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2. <html xmlns="http://www.w3.org/1999/xhtml" >
3. <head>
4. <title>Simple XMLHttpRequest page</title>
5. <script type="text/javascript">
6.
var xmlHttp;
7.
function createXMLHttpRequest ()
8.
{
9.
if (window.ActiveXObject)
10.
{
11.
xmlHttp = new ActiveXObject ("Microsoft.XMLHttp");
12.
}
151
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
else if (window.XMLHttpRequest)
{
xmlHttp = new XMLHttpRequest ();
}
}
function startRequest()
{
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open ("GET","Handler.ashx", true);
//
xmlHttp.send(null);
xmlHttp.send();
}
function handleStateChange()
{
if (xmlHttp.readyState ==4)
{
if (xmlHttp.status ==200)
{
alert ("Server odpowiada: " + xmlHttp.responseText);
}
}
}
</script>
</head>
<body>
<input type=button value= "Startuj asynchroniczna wymiane
danych " onclick = "startRequest();"
40.
/>
41.
</body>
42.
</html>
linii 39 jest wyznaczona funkcja klienta startRequest(), która będzie uruchomiana
W
przy
klikniejciu przycisku „Startuj...” na s przeglądarce. Funkcja StartReqest () w linii 20
uruchomi funkcję createXMLHttpRequest ()(linie 7-17).W liniach 11 lub 15 został
stworzony obiekt XMLHttpRequest. W linii 21
do obrabiania zdarzeń obiektu
XMLHttpRequest na poziomie klienta została podłączona funkcja z nazwą
handleStateChange(). Ta funkcja będzie monitorować stany obiektu XHR. W Linii 22
zostanie przekazane polecenie GET do strony Handler.ashx na serwerze, która zawiera
metodę dla obsługiwania zdarzeń. Funkcja handleStateChange() jest wyznaczona w liniach
26-35. W linii 28 dla stanu obiektu XMLHttpRequest readyState sprawdzamy czy jest
zakończona już operacja ( xmlHttp.readyState ==4). Strona MyHandler.ashx jest
przeznaczona dla obsługiwania zdarzenia i została stworzona w VS2005 jako plik typu
handler (Generic Handler). Kod tego pliku jest pokazany w listingu MyHandler.ashx.
Listing MyHandler.ashx.
<%@ WebHandler Language="VB" Class="Handler" %>
Imports System
Imports System.Web
Public Class Handler : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements
IHttpHandler.ProcessRequest
context.Response.ContentType = "text/plain"
context.Response.Write(" Hello World ")
End Sub
152
Public ReadOnly Property IsReusable() As Boolean Implements
IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Przy realizacji metody GET metoda ProcessRequest() klasy Handler zwraca dokument typu
text/plain z łańcuchem znaków „Hello World”.
2.Wykorzystanie API Script Callback (zwrotne wywołania stron).
ASP.NET 2.0 zawiera integrowany API który nazywa się ASP.NET Script Callback. Ten
interfejs może być wykorzystany dla realizacji poza strefowych wywołań klienta do stron
aplikacji WWW. Wywołanie poza strefowe nie jest zwykłym poleceniem protokołu HTTP.
Żądanie w tym przypadku poczyna się na stronie klienta i powoduje zdarzenie. Stan
początkowy strony klienta jest przekazany do strony serwera. Dodatkowa informacja jest
przekazana przez pola dodatkowe. Na serwerze działa zwykły konwejer obrabiania zdarzeń
strony serwera. Natomiast w tym konwejerze nie ma fazy obrazowania strony - fazy „prerendering”. Zamiast tej fazy będzie uruchomiana metoda CallBack na serwerze. Rezultaty
metody zostaną serializowane oraz przekazane z powrotem do odpowiedniej funkcji
CallBack klienta . Faza obrazowania oraz faza
pre-rendering nigdy nie będą uruchomiane
w tym interfejsu.
Rozpatrzymy szczegółowo wykorzystanie API Script Callback . Zdarzenie na stronie klienta
uruchomi się funkcję JScript z sygnaturą WebForm_DoCallback. Ta funkcja jest
przeznaczona dla obrabiania zdarzenia na poziomie klienta oraz automatyczne dodawana jest
do strony klienta. Funkcja WebForm_DoCallback ma następny stereotyp :
WebForm_DoCallBack (pageID, argument, clientCallback, context, errorCallback,
useAsync);
gdzie:
 pageID – ID strony, która realizuje żądanie na serwerze;
 argument – argument w formacie „String” , który jest przekazany do serwera;
 clientCallback – sygnatura funkcji JavaScript na stronie klienta, która otrzyma
zwrotnie wywołanie od serwera;
 context – dani, którzy będą przekazane do clientCallback;
 errorCallback – sygnatura funkcji na stronie klienta która będzie uruchomiana przy
błędach na poziomie serwera;
 useAsync – kiedy = true, wywołanie realizuje się w sposób asynchroniczny.
Funkcja WebForm_DoCallback skojarzona jest z kontrolką powodującej zdarzenie AJAX.
Niżej pokazane są linie kodu dla kontrolki DropDownList gdzie wyznaczony jest warunek
wywołania funkcji „onchange”.
...
<select name="ddlManufacturers" id="ddlManufacturers"
onchange="WebForm_DoCallback('__Page',document.all['ddlManufacturers'].options(docum
ent.all['ddlManufacturers'].selectedIndex).value,CallbackFunction,'CallbackContext',null,false
);return false;">
<option value="Mercedes">Mercedes</option>
<option value="BMW">BMW</option>
<option value="Renault">Renault</option>
<option value="Toyota">Toyota</option>
<option value="Daewoo">Daewoo</option>
153
…
Funkcja WebForm_DoCallback tworzy obiekt XMLHttpRequest oraz realizuje wszystkie
żądania protokołu HTTP. Scenariusz skryptu WebForm_DoCallback jest przechowywany
w zespole „system.web.dll” oraz będzie połączony ze stroną przez następne dyrektywę :
<script
src="/Ajax_1/WebResource.axd?d=PsGlMi0gSjSlA481o_78hw2&amp;t=6332521126812500
00" type="text/javascript">
</script>
Trzeba wiedzieć że kiedy wywołania poza strefowe są skojarzone z przyciskami, to przycisk
powodujący wywołanie WebForm_DoCallback nie może mieć typ „submit button”. Dla
przycisku typu „submit” zostanie zrealizowane zwykłe przesyłanie formularzy oraz
wywołania wszystkich zdarzeń strony na poziomie serwera bez realizacji technologii AJAX.
Klasy interfejsu API Script Callback inkapsulują odwołania do obiektu XmlHttpRequest. W
kodzie programu nie musi stworzony ten obiekt, wykorzystanie XmlHttpRequest jest
przezroczystym.
Strona serwera musi realizować interfejs System.Web.UI.IcallbackEventHandler:
Partial Class CallBacksite
Inherits System.Web.UI.Page : Implements
System.Web.UI.ICallbackEventHandler
W interfejsie IcallbackEventHandler są
dwie metody: GetCallbackRezult() oraz
RaiseCallbackEvent(eventArgument as string).
Metoda RaiseCallbackEvent(eventArgument as string) jest wywołana pierwszej . W tej
metodzie muszą być realizowane czynności przygotowawcze. Otrzymane od klienta dani
muszą być deserializowane. Metoda GetCallbackRezult() jest wywołana dla zwrotnego
przesyłania danych do strony klienta. Listing CallBacksite zawiera kod strony serwera. W
liniach 5-14 oraz 15-18 są zdefiniowane funkcje GetCallbackRezult() oraz
RaiseCallbackEvent(). W linii 3 jest wyznaczony parametr evArg który będzie zawierał
wartość pola kontrolki DropDownList od klienta.
W linii 24 jest zdefiniowana zmienna argClientFunction, która jest przeznaczona dla
formowania w linii 27 drugiego argumentu funkcji WebForm_DoCallBack(). Ciała
sygnatura funkcji WebForm_DoCallBack() będzie sformowana w zmiennej cScript w linii
28. Kod funkcji CallBackFunction() klienta jest sformowany w zmiennej scr w liniach 30-47.
Dla przeniesienia
tej sygnatury do klienta
jest wykorzystana metoda
RegisterStartupScript () w linii 48. Zadaniem funkcji CallBackFunction() klienta jest
deserializacja danych i ustalenie nowego stanu drugiej kontrolki DropDownList.
Listing CallBacksite.
1. Partial Class CallBacksite
2. Inherits System.Web.UI.Page : Implements
System.Web.UI.ICallbackEventHandler
3. Protected evArg As String
4. #Region "ICallbackEventHandler Members"
5. Public Function GetCallbackResult() As String Implements
System.Web.UI.ICallbackEventHandler.GetCallbackResult
6. 'Throw New Exception("The method or operation is not implemented")
7. BindModels(evArg)
8. evArg = String.Empty
9. Dim i As Integer
154
10.
11.
12.
13.
14.
For i = 0 To ddlModel.Items.Count - 1 Step 1
evArg = evArg + ddlModel.Items(i).ToString + ";"
Next
Return evArg
End Function
15.
Public Sub RaiseCallbackEvent(ByVal eventArgument As String)
Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent
16.
'Throw New Exception("The method or operation is not
implemented")
17.
evArg = eventArgument
18.
End Sub
19.
#End Region
20.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
21.
If Not IsPostBack Then
22.
Me.BindManufacturers()
23.
End If
24.
Dim argClientFunction As String
25.
Dim cScript As String
26.
Dim scr As String
27.
argClientFunction = "document.all['" +
ddlManufacturers.ClientID + "'].options(document.all['" +
ddlManufacturers.ClientID + "'].selectedIndex).value"
28.
cScript = ClientScript.GetCallbackEventReference(Me,
argClientFunction, "CallbackFunction", "'CallbackContext'", "null",
False)
29.
ddlManufacturers.Attributes.Add("onchange", cScript + ";return
false;")
30.
31.
scr = "<script language=javascript>"
scr += "function CallbackFunction(callbackResult,
callbackContext)"
32.
scr += "{"
33.
scr += "
var ddlModel = document.all['" + ddlModel.ClientID
+ "'];"
34.
scr += "
var l = ddlModel.options.length;"
35.
scr += "
for(var i=0; i<l; i++)"
36.
scr += "
ddlModel.options.remove(0);"
37.
scr += "
var models = callbackResult.split(';');"
38.
scr += "
for(var i=0; i<models.length; i++)"
39.
scr += "
{"
40.
scr += "
var oOption =
document.createElement(""OPTION"");"
41.
scr += "
oOption.text = models[i];"
42.
scr += "
oOption.value = models[i];"
43.
scr += "
ddlModel.options.add(oOption);"
44.
scr += "
}"
45.
scr += "
return false;"
46.
scr += "}"
47.
scr += "</script>"
48.
Me.RegisterStartupScript("scr" + Me.ClientID.ToString, scr)
49.
End Sub
50.
Sub BindManufacturers()
51.
52.
53.
54.
55.
56.
ddlManufacturers.Items.Add(New
ddlManufacturers.Items.Add(New
ddlManufacturers.Items.Add(New
ddlManufacturers.Items.Add(New
ddlManufacturers.Items.Add(New
End Sub
ListItem("Mercedes"))
ListItem("BMW"))
ListItem("Renault"))
ListItem("Toyota"))
ListItem("Daewoo"))
155
57.
Sub BindModels(ByVal manufacturer As String)
58.
Select Case manufacturer
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
Case "Mercedes"
ddlModel.Items.Clear()
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
'break()
Case "BMW"
ddlModel.Items.Clear()
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
'break()
Case "Renault"
ddlModel.Items.Clear()
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
'break;
Case "Toyota"
ddlModel.Items.Clear()
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
ddlModel.Items.Add(New
'break;
Case "Daewoo"
ddlModel.Items.Clear()
ddlModel.Items.Add(New
ddlModel.Items.Add(New
'break;
End Select
End Sub
End Class
ListItem("S350"))
ListItem("S500"))
ListItem("S600"))
ListItem("CLK"))
ListItem("model 3"))
ListItem("model 5"))
ListItem("model 7"))
ListItem("X3"))
ListItem("X5"))
ListItem("12"))
ListItem("19"))
ListItem("21"))
ListItem("Aristo"))
ListItem("Avalon"))
ListItem("Avensis"))
ListItem("Bandeirante"))
ListItem("Sens"))
ListItem("Lanos"))
Wykorzystanie kontrolek biblioteki MS ASP.NET 2.0 AJAX.
W celu polepszenia pracy projektanta w API MS ASP.NET 2.0 AJAX są stworzone następne
kontrolki:
 ScriptManager
 UpdatePanel
 UpdateProgress
 Timer
Nazwane powyżej kontrolki mogą być rozmieszczone na stronie aspx w sposób podobny
innym kontrolkom ASP.NET. Wykorzystanie kontrolek AJAX pozwoli implementować do
interfejsu AJAX inne kontrolki ASP.NET w sposób przezroczysty, bez konieczności
stworzenia obiektów XMLHttpRequest. W tym przypadku także niema potrzeby tworzyć
kody dla metod wywołań zwrotnych.
Każda strona aplikacji AJAX musi zostać wyposażona w jeden egzemplarz kontrolki
ScriptManager. ScriptManager jest centralnym elementem biblioteki MS ASP.NET 2.0 AJAX
oraz implementuje klasy i metody dla zarządzania wszystkimi elementami dostępnymi na
156
stronie i pochodzącymi z tejże biblioteki. Ten element koordynuje i rejestruje skrypty na
poziomie klienta odpowiedzialne za częściowe odświeżanie strony.
Na rys.1 są pokazane kontrolki używane w projekcie aplikacji AJAX.
Właściwości kontrolki ScriptManager jest pokazane na rys. 2. Trzeba zwrócić uwagę na
właściwość EnablePartialRendering, która umożliwia korzystanie z technologii AJAX na
stronie.
Rys.1
157
Rys.2
158
Kontrolka UpdatePanel realizuje implementację mechanizmów częściowego odświeżania
strony, dzięki czemu możliwe jest ograniczenie czasu ponownego lądowania strony.
UpdatePanel jest kontrolką będącą kontenerem elementów podlegających dynamicznemu
odświeżaniu na stronie. Na rys.3 są pokazane właściwości kontrolki UpdatePanel1 w
projekcie.
Rys.3
Każda kontrolka UpdatePanel zawiera szablon ContentTemplate, widoczny tylko w kodzie
XML Script projektu. Ten szablon stanowi kontener kontrolek podlegających dynamicznemu
odświeżaniu strony. Dodawanie kontrolek
do tej strefy polega na przeciągnięciu
odpowiedniego elementu z panelu narzędzi do panelu kontrolki UpdatePanel.
Wykorzystanie kontrolki UpdatePanel jako wyznaczonego obszaru elementów interfejsu
strony, dynamiczne odświeżanych bez potrzeby ponownego ładowania strony jest powiązane
z procesem dodawania kontrolek bezpośrednio do szablonu ContentTemplate. Wszystkie
zdarzenia generowane przez kontrolki będące częścią UpdatePanelu nie powodują
ponownego przeładowania calej strony – ich działanie odnosi się tylko do panelu. Kontrolka
UpdatePanel zawiera kolekcję Triggers. Tryger odpowiada zdarzeniu które powoduje
odświeżanie panelu. Trygery mogą być dwóch typów:
 AsyncPostBackTrigger
 PostBackTrigger
Na rys. 4 jest pokazane okno dla definicji trygerów.
Bardzo ważnym rzeczą , jest rodzaj reakcji kontrolki UpdatePanel na odświeżanie strony,
której częścią jest dany egzemplarz klasy UpdatePanel . Zachowanie to zależy od wartości
właściwości UpdateMode i przedstawia się następująco:
1. Jeśli wartość właściwości UpdateMode jest ustawiona na Always (wartość po
domyśleniu) , zawartość panelu odświeżana jest każdorazowo w sytuacji gdy
następuje
ponowne ładowanie calej zawartości strony zawierającej kontrolkę
UpdatePanel.
2. Jeśli wartość właściwości UpdateMode jest ustawiona na Conditional, zawartość
panelu odświeżana jest w następujących okolicznościach:
 Wywoływana jest metoda Update() panelu.
 Wywołanie powodowane jest przez kontrolkę definiowaną jako trigger.
159
Rys. 4.
AsyncPostBackTrigger definiuje zdarzenie odświeżania panelu, które związane jest z
kontrolką nie będącą częścią tego panelu. Wywołanie zdarzenia przypisanego do trygeru
dokonuje zmian wewnątrz panelu i nie generuje zdarzenia PostBack strony. Zdarzeniem
kontrolki powodującej odświeżanie zawartości panelu, może być np. kontrolka Button i
zdarzenie Click_Button.
PostBackTrigger - definiuje zdarzenie odświeżania panelu oraz generuje zdarzenie PostBack
strony.
W następnym listingu jest pokazany kod XML Script projektu.
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ajax</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False" DataKeyNames="UserID"
DataSourceID="AccessDataSource1" PageSize="4"
AllowPaging="True" BackColor="#FFE0C0" BorderColor="Navy"
BorderStyle="Groove">
<Columns>
<asp:BoundField DataField="UserID"
HeaderText="UserID" InsertVisible="False" ReadOnly="True"
SortExpression="UserID" >
<HeaderStyle BackColor="#FFC0FF" />
</asp:BoundField>
<asp:BoundField DataField="FirstName"
HeaderText="FirstName" SortExpression="FirstName" />
<asp:BoundField DataField="LastName"
HeaderText="LastName" SortExpression="LastName" />
160
<asp:BoundField DataField="Address"
HeaderText="Address" SortExpression="Address" />
<asp:BoundField DataField="City"
HeaderText="City" SortExpression="City" />
<asp:BoundField DataField="State"
HeaderText="State" SortExpression="State" />
<asp:BoundField DataField="ZIP"
HeaderText="ZIP" SortExpression="ZIP" />
<asp:BoundField DataField="Phone"
HeaderText="Phone" SortExpression="Phone" />
</Columns>
<HeaderStyle BackColor="#FFC0FF" />
</asp:GridView>
<asp:AccessDataSource ID="AccessDataSource1"
runat="server" DataFile="C:\aspnet\aspnet_bazy_test\banking.mdb"
SelectCommand="SELECT [UserID], [FirstName],
[LastName], [Address], [City], [State], [ZIP], [Phone] FROM [tblUsers]">
</asp:AccessDataSource>
</ContentTemplate>
</asp:UpdatePanel>
</div>
<asp:UpdateProgress ID="UpdateProgress1" runat="server"
AssociatedUpdatePanelID="UpdatePanel1">
<ProgressTemplate>
Pobieram pracownikow...
</ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="UpdatePanel2" runat="server"
EnableViewState="False">
<ContentTemplate>
Zegarek wewnątrz panelu UpdatePanel :<asp:Label ID="Label1"
runat="server" Text="Label"></asp:Label>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger />
</Triggers>
</asp:UpdatePanel>
<asp:Button ID="Button1" runat="server" Text="Button" />&nbsp;
<asp:Label ID="Label2" runat="server" Text="Label" style="left: 5px; top: -22px" Width="74px"></asp:Label>&nbsp;
</form>
</body>
</html>
W następnym listingu pokazany jest kod klasy pośredniej.
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub GridView1_PageIndexChanged(ByVal sender As Object, ByVal
e As System.EventArgs) Handles GridView1.PageIndexChanged
System.Threading.Thread.Sleep(3000)
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As
System.EventArgs) Handles Me.Load
Me.Label1.Text = DateTime.Now.ToString
Me.Label2.Text = "Zegarek z zewnątrz panelu UpdatePanel: " &
DateTime.Now.ToString
End Sub
End Class
161
Kontrolka UpdateProgress jest powiązana z kontrolką UpdatePanel i umożliwia wykonywania
określonych działań (wcześniej zdefiniowanych przez programistę) podczas odświeżania
zawartości panelu. Tymi działaniami mogą być np. wyświetlanie danego tekstu. Elementy,
stanowiące zawartość tej kontrolki jak również sama kontrolka nie są widoczne w
przeglądarce do czasu rozpoczęcia odświeżania panelu i są widoczny tylko w czasie trwania
tego procesu. Powiązanie z kontrolką UpdateProgress może być zrealizowane przez
właściwość AssociatedUpdatePanelID. W przypadku, kiedy powiązanie nie zostanie
zdefiniowane, czynności wyznaczone w UpdateProgress będą uruchomiane dla każdego
elementu UpdatePanel .
Technologia Web Parts w ASP.NET
Technologia Web Parts jest zintegrowaną częścią ASP.NET 2.0 udostępnianą poprzez
zestaw kontrolek serwerowych, które pozwalają użytkownikowi dostosować wygląd
strony do własnych upodobań. Można modyfikować:

Zawartość – użytkownik może określić, które elementy mają być widoczne na
stronie, może je dodawać lub usuwać.

Wygląd – można zmieniać wygląd kontrolek oraz przenosić je w inne miejsce
strony (drag-and-drop)

Zachowanie – poprzez specjalnie zdefiniowane właściwości można określać
zachowanie kontrolek,
Wszystkie te ustawienia są ściśle związane z mechanizmem personalizacji. Wprowadzone
modyfikacje zapisywane są w odpowiedniej strukturze tabel wygenerowanych podczas
konfiguracji ASP.NET.
Kontrolki Web Parts są to dowolne kontrolki dostępne w ASP.NET, umieszczone w
odpowiednich strefach, tzw. Zones.
Komponenty Web Parts

WebPartManager – zarządza wszystkimi elementami Web Parts na stronie. Nie
posiada on interfejsu graficznego i nie jest widoczny dla użytkownika.

WebPartZone – przechowuje widoczne elementy Web Parts, określa ich domyślny
układ oraz zachowanie. Umożliwia użytkownikowi przenoszenie kontrolek do
innych stref typu WebPartZone

EditorZone – zawiera edytor dla poszczególnych elementów WebParts, widoczny
dopiero po przełączeniu strony w tryb edycji

CatalogZone – zawiera elementy opcjonalne, które początkowo nie są widoczne
ale mogą zostać dodane do wybranej strefy WebPartZone . Podobnie jak
162
EditorZone strefa ta nie jest widoczna domyślnie, a dopiero po przełączeniu w
odpowiedni tryb

ConnectionsZone – umożliwia tworzenie połączeń pomiędzy elementami Web
Parts
Rys. 1 Przykładowy układ elementów WebParts na stronie
Dostępne tryby pracy z elementami Web Parts:

Browse - normalne przeglądanie strony, bez dodatkowych opcji

Design – umożliwia zarządzanie układem elementów na stronie,

Editor – w tym trybie można edytować właściwości wybranych elementów

Catalog – umożliwia pokazanie bądź ukrycie elementów dodatkowych, domyślnie
ukrytych

Connect – edycja połączeń pomiędzy elementami
Dostęp do poszczególnych trybów zależy od uprawnień przypisanych danemu
użytkownikowi.
Są dwa rodzaje zasięgu personalizacji:

UserScope – domyślny dla wszystkich zalogowanych użytkowników, zmiany
wprowadzone w tym trybie dotyczą tylko jednego użytkownika i nie są widoczne
przez innych.
163

SharedScope – dostępny dla użytkowników którym przydzielono prawa do tego
trybu, wprowadzone zmiany widoczne są przez wszystkich użytkowników; tryb ten
umożliwia administratorom systemu edycję i dostosowanie układu stron całego
portalu.
Etapy tworzenia strony wykorzystującej Web Parts.
Dla lepszego zobrazowania elementów Web Parts przedstawione zostaną kolejne etapy
tworzenia strony wykorzystującej Web Parts. Całość przykładowej aplikacji znajduje się
na dołączonej płycie CD.
Utworzenie pustej strony – strona ta zostanie wykorzystana do prezentacji
możliwości Web Parts
Uruchomienie Microsoft Visual Studio
Wybranie opcji New Web Site z menu File
Wybranie typu projektu jako ASP.NET Web Site, a język programowania C#
podanie nazwy i lokalizacji dla strony i OK
Przygotowanie strony – ustwienie układu graficznego strony i przygotowanie miejsca
gdzie będą dodawane kolejne elementy
Otwarcie pliku Default.aspx trybie Design
Wstawienie tabeli – opcja Layout->Insert Table, tabela o 3 wierszach i 3 kolumnach,
szerokość tabeli 100%
Przygotowanie układu strony – połączenie kolumn w wierszu 1. oraz 2. (opcja Merge
Cells w menu kontektowym), dodanie do pierwszego wiersza etykiety z tytułem strony
Kod tabeli:
164
<table style="width: 100%;">
<tr>
<td align="left" colspan="3" style="background-color: steelblue;">
<asp:Label ID="lblHeader" runat="server" ForeColor="Cyan" Text="Web Parts
test" Font-Bold="True" Font-Names="Verdana" Font-Size="18pt"></asp:Label>
</td>
</tr>
<tr>
<td align="right" colspan="3"></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
Dodanie elementu WebPartManager
Wybranie z panelu Toolbox komponentu WebPartManager i dodanie go przed tabelą.
Komponent ten musi znajdować się na górze strony, przed innymi elementami.
Dodanie stref WebPartZone
Wybranie z panelu Toolbox komponentu WebPartZone i dodanie go do pierwszej
kolumny ostatniego wiersza.
Określenie nazwy komponentu jako LeftZone
Wybranie jednego ze zdefiniowanych dla niego styli, np.: Professional
Dodanie drugiej strefy do trzeciej kolumny ostatniego wiersza, nazwanie jej RightZone i
określenie stylu Professional
Komponenty WebPartZone będą stanowiły pewnego rodzaju kontenery na poszczególne
elementy WebPart
165
Dodanie pierwszego elementu WebPart – elementami WebParts są dowolne
kontrolki dostępne w ASP.NET, a więc zarówno kontrolki serwerowe jak i kontrolki
zdefiniowane
Dodanie do strefy LeftZone kontrolki Calendar
Przełączenie widoku w tryb Source i ustawienie tytułu kontrolki poprzez zdefiniowanie
atrybutu Title=”Kalendarz” dla dodanego komponentu Calendar w <ZoneTemplate>
Dodanie kolejnych kontrolek typu WebPart - zdefiniowanie własnych kontrolek,
które będą mogły być wykorzystane na stronie
Kliknięcie prawym przyciskiem myszy na głównym węźle projektu i wybranie opcji Add
New Item z menu kontekstowego
Wybranie typu Web User Control i kliknięcie Add
Utworzenie przykładowej kontrolki Search.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Search.ascx.cs" Inherits="Search"
%>
<table width="100%">
<tr>
<td align="right">
<asp:TextBox ID="TextBox1" runat="server" Font-Names="Verdana" Font-Size="8pt">
</asp:TextBox>
</td>
</tr>
<tr>
<td align="right">
<asp:Button ID="Button1" runat="server" Font-Names="Verdana" Font-Size="8pt" Text="Szukaj"
/>
</td>
</tr>
<tr>
<td align="right">
166
<asp:HyperLink ID="HyperLink1" runat="server" Font-Names="Verdana" Font-Size="8pt">
Szukanie zaawansowane</asp:HyperLink>
</td>
</tr>
</table>
Utworzenie drugiej kontrolki Ankieta.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Ankieta.ascx.cs"
Inherits="WebUserControl" %>
<table style="width: 200px">
<tr>
<td><asp:Label ID="Label1" runat="server" Text="Czy podoba Ci się ASP.NET ?" FontNames="Verdana" Font-Size="8pt"></asp:Label>
</td>
</tr>
<tr>
<td style="padding-left: 20px">
<asp:RadioButtonList ID="RadioButtonList1" runat="server" Font-Names="Verdana" FontSize="8pt">
<asp:ListItem>Tak</asp:ListItem>
<asp:ListItem>Nie</asp:ListItem>
<asp:ListItem>Jeszcze nie wiem</asp:ListItem>
</asp:RadioButtonList>
</td>
</tr>
</table>
Otworzenie pliku Default.aspx w trybie Design i dodanie nowych kontrolek do strefy
RightZone
Określenie tytułów tych kontrolek poprzez zdefiniowanie atrybutów Title dla
komponentów dodanych do <ZoneTemplate>
<asp:WebPartZone ID="RightZone" ... >
...
<ZoneTemplate>
<uc1:Search Title="Szukaj" ID="Search1" runat="server" />
<uc2:Ankieta Title="Ankieta" ID="Ankieta1" runat="server" />
</ZoneTemplate>
</asp:WebPartZone>
Uruchomienie strony - Ctrl+F5
167
W tym momencie można już minimalizować lub zamykać poszczególne elementy za
pomocą menu rozwijanego, tzw. Verbs menu
Umożliwienie zmiany trybu pracy z elementami WebParts
Dodanie odnośników, za pomocą których będą przełączane tryby – modyfikacja
drugiego wiersza tabeli na stronie
...
<tr>
<td align="right" colspan="3" style="background-color: gold;">
<asp:LinkButton ID="linkBtnBrowse" ForeColor="Maroon" Text="Browse" style="Text-Decoration:
none" Runat="server" Font-Bold="True" Font-Names="Verdana" Font-Size="8pt" /> |
<asp:LinkButton ID="linkBtnDesign" ForeColor="Maroon" Text="Design" style="Text-Decoration:
none" Runat="server" Font-Bold="True" Font-Names="Verdana" Font-Size="8pt" /> |
<asp:LinkButton ID="linkBtnCatalog" ForeColor="Maroon" Text="Catalog" style="TextDecoration:
none" Runat="server" Font-Bold="True" Font-Names="Verdana" Font-Size="8pt"/> |
<asp:LinkButton ID="linkBtn_Edit" ForeColor="Maroon" Text="Edit" style="Text-Decoration:
none"
Runat="server" Font-Bold="True" Font-Names="Verdana" Font-Size="8pt"/> |
<asp:LinkButton ID="linkBtnReset" ForeColor="Maroon" Text="Reset" style="Text-Decoration:
none" Runat="server" Font-Bold="True" Font-Names="Verdana" Font-Size="8pt" />
</td>
</tr>
....
Zdefiniowanie obsługi zdarzenia kliknięcia na poszczególne odnośniki – kliknąć
dwukrotnie na każdy z odnośników – utworzone zostaną metody obsługujące
zdarzenia
Dodanie odpowiedniego kodu do poszczególnych metod
protected void linkBtnBrowse_Click(object sender, EventArgs e)
{
WebPartManager1.DisplayMode = WebPartManager.BrowseDisplayMode;
}
protected void linkBtnDesign_Click(object sender, EventArgs e)
{
WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode;
168
}
protected void linkBtnCatalog_Click(object sender, EventArgs e)
{
WebPartManager1.DisplayMode = WebPartManager.EditDisplayMode;
}
protected void linkBtnEdit_Click(object sender, EventArgs e)
{
WebPartManager1.DisplayMode = WebPartManager.CatalogDisplayMode;
}
protected void linkBtnReset_Click(object sender, EventArgs e)
{
PersonalizationAdministration.ResetUserState("~/Default.aspx");
}
Uruchomienie strony - Ctrl+F5
Przełączenie w tryb Design (tryby Catalog oraz Edit nie są jeszcze zaimplementowane)
Można już zmieniać położenie poszczególnych komponentów łapiąc je i przenosząc w inne
miejsce (drag-and-drop)
169
Wybranie opcji Reset spowoduje powrót do ustawień początkowych
Dodanie komponentu CatalogZone – przechowywać on będzie elementy dodatkowe, które mogą
być pokazane na stronie, ale nie muszą
Wybranie z panelu Toolbox komponentu CatalogZone i dodanie go jednej ze stref, np.:
LeftZone,
Można zastosować jeden z dostępnych styli graficznych, np.: Professional
Zdefiniowanie nowej kontrolki – Description.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Description.ascx.cs"
Inherits="PopularLinks" %>
<asp:Label ID="lblDesc" runat="server" Font-Names="Verdana" Font-Size="8pt"
Text="Technologia Web Parts jest zintegrowaną częścią ASP.NET 2.0 udostępnianą poprzez zestaw
kontrolek serwerowych, które pozwalają użytkownikowi dostosować wygląd strony do własnych
upodobań."
Width="199px"></asp:Label>
Dodanie komponentu DeclarativeCatalogPart na CatalogZone
Dla DeclarativeCatalogPart wybranie opcji EditTemplate i przeciągnięcie kontrolki
Description.ascx. Kod dla elementu CatalogZone powinnien wyglądać mniej więcej tak
jak poniżej.
<asp:CatalogZone ID="CatalogZone1" ... >
...
<ZoneTemplate>
<asp:DeclarativeCatalogPart ID="DeclarativeCatalogPart1" runat="server">
<WebPartsTemplate>
<uc3:Description Title="Opis" ID="Description1" runat="server" />
</WebPartsTemplate>
</asp:DeclarativeCatalogPart>
</ZoneTemplate>
</asp:CatalogZone>
170
Uruchomienie strony - Ctrl+F5
Można teraz zaznaczyć dodatkową kontrolkę i dodać ją do jednej ze stref
Elementy dostępne poprzez CatalogZone mogą być usuwane za pomocą opcji w menu
(Verbs menu)
171
Dodanie komponentu EditorZone – umożliwi on edycję wyglądu i zachowania poszczególnych
elementów
Wybranie z panelu Toolbox komponentu EditorZone i dodanie go jednej ze stref, np.:
LeftZone,
Można zastosować jeden z dostępnych styli graficznych, np.: Professional
Dodanie do EditorZone komponentu AppearanceEditorPart (umożliwi edycję pewnych
cech wyglądu
Uruchomienie strony - Ctrl+F5
Wybranie trybu Edit
Wybranie opcji Edit z menu (Verbs menu) dla danej kontrolki Web Part, np.: dla Szukaj
Jak widać istnieje teraz możliwość określenia tytułu kontrolki, stylu oraz rozmiaru.
Można, np.: usunąć pasek z tytułem bądź obramowanie.
172
Zmiana wyglądu kontrolki – określenie nowego tytułu
Edycja zdefiniowanych przez użytkownika właściwości
Dodanie do EditorZone komponentu PropertyGridEditorPart (służy do edycji specjalnie
zdefiniowanych właściwości elementów Web Parts)
Określenie edytowalnej właściwości dla kontrolki Description.ascx - aby właściwości
kontrolek były edytowalne należy je zdefiniować z atrybutami Personalizable oraz
WebBrowsable. Kod pliku Description.ascx.cs powinien wyglądać tak:
public partial class UserControls_NewProducts : System.Web.UI.UserControl
{
private string _desc = "";
[Personalizable(), WebBrowsable, WebDisplayName("Opis")]
public string Description
{
get { return _desc; }
set { _desc = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
if (_desc != "")
lblDesc.Text = _desc;
}
}
Uruchomienie strony - Ctrl+F5
Jeśli element Opis jest niewidoczny, przełączenie w tryb Catalog i dodanie jej do jednej
ze stref.
Wybranie trybu Edit
Wybranie opcji Edit z menu (Verbs menu) dla kontrolki Opis
173
Poprzez komponent PropertyGridEditorPart jest możliwość edycji wcześniej zdefiniowanej
właściwości. Element Kalendarz został zamknięty (opcja Close w Verbs menu.
Zdefiniowanie nowego opisu dla kontrolki
Kliknięcie Ok i zmiana trybu na Browse
W kontrolce widoczny jest nowy opis zdefiniowany przez właściwość.
174
Zmiana zasięgu personalizaji – zmiana z UserScope (zmiany widoczne tylko dla jednego
uzytkownika) na SharedScope (zmiany widoczne przez wszystkich uzytkowników)
Zdefiniowanie w pliku konfiguracyjnym Web.config uprawnień dla użytkowników
mogących korzystać z personalizacji typu SharedScope
<system.web>
...
<webParts>
<personalization defaultProvider="AspNetSqlPersonalizationProvider">
<authorization>
<!--<allow roles="Administratorzy" verbs="enterSharedScope" />-->
<allow users="*" verbs="enterSharedScope" />
</authorization>
</personalization>
</webParts>
...
</system.web>
Ponieważ ta prosta przykładowa aplikacja nie zawiera mechanizmu logowania, prawo do
korzystania z personalizacji typu SharedScope nadane zostało wszystkim użytkownikom.
Jednak w rzeczywistości najlepiej jest je przypisywać tylko określonej roli, np.:
Administratorom.
Dodanie w pliku Default.aspx kodu przełączającego tryb personalizacji SharedScope
protected void Page_Load(object sender, EventArgs e)
{
if (WebPartManager1.Personalization.CanEnterSharedScope &&
WebPartManager1.Personalization.Scope == PersonalizationScope.User)
WebPartManager1.Personalization.ToggleScope();
}
Web Parts udostępniają bardzo bogaty zestaw możliwości znacznie rozszerzających
funkcjonalność aplikacji.
175

Podobne dokumenty