architektura .net oraz asp.net

175
1 Prof. dr. Hab. Inż. Włodzimierz Khadzhynov Programowanie w środowisku .NET

Upload: ngotruc

Post on 11-Jan-2017

239 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Architektura .NET oraz ASP.NET

1

Prof. dr. Hab. Inż. Włodzimierz Khadzhynov

Programowanie w środowisku .NET

Page 2: Architektura .NET oraz ASP.NET

2

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

Page 3: Architektura .NET oraz ASP.NET

3

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

Page 4: Architektura .NET oraz ASP.NET

4

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

Win32Win32

MessageMessage

QueuingQueuing

COM+COM+

(Transactions, Partitions, (Transactions, Partitions,

Object Pooling)Object Pooling)IISIIS WMIWMI

Common Language RuntimeCommon Language Runtime

.NET Framework Class Library.NET Framework Class Library

ADO.NET and XMLADO.NET and XML

XML Web ServicesXML Web Services User InterfaceUser Interface

VisualBasic

C++ C#

ASP.NETASP.NET

Perl Python …

Rys.1

Page 5: Architektura .NET oraz ASP.NET

5

.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).

Page 6: Architektura .NET oraz ASP.NET

6

C# VB.NET C++.NET Inne

Kod pośredni kompilacji:

Metadane +MSIL

Win 2000 WinXP Linux Inne

1 etap kompilacji

2 etap kompilacji

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.

Page 7: Architektura .NET oraz ASP.NET

7

(Demonstracja -> D:\2310B\Media\2310B_01A001.htm)

Schemat etapów kompilacji jest pokazany na rys. 4.

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.

Kompilacja

Program Kod pośredni MSIL i metadane

CLR - Common Language Runtime

Aplikacja

Page 8: Architektura .NET oraz ASP.NET

8

WorkProcess ASP.NET

AppDomain

Polecenie HTTP:

GET /foo/foo.aspx

Odpowiedz HTTP:

HTTP/ Hello World!

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>

<%

Page 9: Architektura .NET oraz ASP.NET

9

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

Page 10: Architektura .NET oraz ASP.NET

10

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.

Page 11: Architektura .NET oraz ASP.NET

11

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>

<asp:Button id="btn" runat="server"/>

...

</HTML>

<SCRIPT Language="vb" runat="server">

Sub btn_Click(s As Object, e As EventArgs)

Handles btn.Click

...

End Sub

</SCRIPT>

<HTML>

<asp:Button id="btn" runat="server"/>

...

</HTML>

<SCRIPT Language="vb" runat="server">

Sub btn_Click(s As Object, e As EventArgs)

Handles btn.Click

...

End Sub

</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 Code-

behind code jest możliwość niezależnego projektowania interfejsu użytkownika i logiki

biznesowej.

Rys.8

Page 12: Architektura .NET oraz ASP.NET

12

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.

Page 13: Architektura .NET oraz ASP.NET

13

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 As

System.EventArgs) Handles MyBase.PreRender

Trace.Write("Page_PreRender Event Handler", "Teraz jestem w

Page_PreRender event handler!")

End Sub

Private Sub Page_Unload(ByVal sender As Object, ByVal e As

System.EventArgs) Handles MyBase.Unload

Trace.Write("Page_Unload Event Handler", "Teraz jestem w

Page_Unload event handler!")

End Sub

End Class

Kod strony event0.aspx zawiera tylko jedną dyrektywę Page:

Listing listingu event0.asp.

Page 14: Architektura .NET oraz ASP.NET

14

<%@ Page Trace="True" CodeBehind="event0.aspx.vb" Language="vb"

AutoEventWireup="false" Inherits="events.event0" %>

<!- demonstracja zdarzen strony PAGE->

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.

Page 15: Architektura .NET oraz ASP.NET

15

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. static void Main(string[] args)

12. {

13. MessagePrintDelegate mpDel = new

14. MessagePrintDelegate(PrintMessage);

15. GetCountDelegate gcd = new GetCountDelegate(GetCount);

16. int count = gcd(new Person(), new Person());

17. Console.WriteLine("Otrzymano wartość: {0}", count);

18. LongRunningDelegate lrd = new

19. LongRunningDelegate(LongRunningMethod);

20. lrd(mpDel);

21. Console.ReadLine();

22. }

23. static void LongRunningMethod(MessagePrintDelegate mpd)

24. {

25. for (int i = 0; i < 99; i++)

26. {

27. if (i % 10 == 0)

28. {

29. mpd(string.Format("Praca w toku... Wykonano {0}%", i));

30. }

31. }

32. }

33. static int GetCount(object obj1, object obj2)

34. {

35. // wykonaj jakieś operacje

36. Random rnd = new Random();

37. return rnd.Next();

38. }

39. static void PrintMessage(string msg)

40. {

41. Console.WriteLine("[{0}] {1}",

42. DateTime.Now.ToShortTimeString(), msg);

43. }

44. }

45. class Person

46. {

47. }

48. class Contact : Person

49. {

50. }

51. }

Page 16: Architektura .NET oraz ASP.NET

16

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.

Page 17: Architektura .NET oraz ASP.NET

17

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. Me.TestEvents()

25. End Sub

26. End Class 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

Page 18: Architektura .NET oraz ASP.NET

18

1. Public Class WebForm2 2. Inherits System.Web.UI.Page 3. #Region " Web Form Designer Generated Code " 4. <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.

Page 19: Architektura .NET oraz ASP.NET

19

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. Public Class WebForm1 Inherits System.Web.UI.Page

28. Sub TestEvents()

29. Dim Obj As New Class1

30. ' Associate an event handler with an event.

31. AddHandler Obj.Ev_Event, AddressOf myevents

32. 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

Page 20: Architektura .NET oraz ASP.NET

20

43. End Class

44. Protected WithEvents Label1 As System.Web.UI.WebControls.Label

45. …

46. 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. Me.TestEvents()

51. End Sub

52. End Class

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. Public Class WebForm2

28. Inherits System.Web.UI.Page

29. #Region " Web Form Designer Generated Code "

30. <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!"

Page 21: Architektura .NET oraz ASP.NET

21

48. End Sub

49. 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ź”.

Page 22: Architektura .NET oraz ASP.NET

22

Serwer1.Tworzy elementy

interfejsu

2.Wprowadzanie

danych i wysyłanie

formularza

4 Otrzymuje

dane

3.Wysylanie

danych

Klient

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.

Page 23: Architektura .NET oraz ASP.NET

23

4.Wysłanie

danych z

powrotem do

serwera

Serwer:

3. Wypelnianie

formularza i wysłanie

1.Tworzenie

elementów

interfejsu użytkownika

2.Wysylanie

elementów w

kodzie HTML

Klient:

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>

Page 24: Architektura .NET oraz ASP.NET

24

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_LoadPage_Load

Page_UnloadPage_Unload

Textbox1_ChangedTextbox1_Changed

Button1_ClickButton1_Click

Page is disposed

Page_InitPage_Init

Control eventsControl events

Change EventsChange Events

Action EventsAction Events

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

Page 25: Architektura .NET oraz ASP.NET

25

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

Page.IsPostBack prevents reloading for each postback

Private Sub Page_Load(ByVal s As System.Object, _

ByVal e As System.EventArgs) _

Handles MyBase.Load

If Not Page.IsPostBack Then

'executes only on initial page load

End If

'this code executes on every request

End Sub

Private Sub Page_Load(ByVal s As System.Object, _

ByVal e As System.EventArgs) _

Handles MyBase.Load

If Not Page.IsPostBack Then

'executes only on initial page load

End If

'this code executes on every request

End Sub

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

Page 26: Architektura .NET oraz ASP.NET

26

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 "

Page 27: Architektura .NET oraz ASP.NET

27

'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

Page 28: Architektura .NET oraz ASP.NET

28

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:

1. 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ń.

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.

Page 29: Architektura .NET oraz ASP.NET

29

Podanie danych

uwierzytelnienia

jest

Uwierzytelniony?

Próba uzyskania

dostępu do

zasobów

Brak dostępu

Autoryzowany?

Personalizacja

danej tożsamości

Nie

Nie

Tak

Tak

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.

Page 30: Architektura .NET oraz ASP.NET

30

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>

SerwerKlient

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ę

Page 31: Architektura .NET oraz ASP.NET

31

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

Page 32: Architektura .NET oraz ASP.NET

32

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) :

Page 33: Architektura .NET oraz ASP.NET

33

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.

Page 34: Architektura .NET oraz ASP.NET

34

Użytkownik zgłasza żądanie

IISZezwala na przekazanie

żądania

Użytkownik

uzyskuje prawo

dostępu do zasobu

Czy jest

dostępne cookie

autoryzacyjne?

Przekierowanie do

formularza

logowania

Pobranie danych

uwierzytelniających

Czy jest

uwierzytelniony?

Brak dostępu

Stworzenie cookie

autoryzacyjnego

Tak

Nie

Tak

Nie

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.

Page 35: Architektura .NET oraz ASP.NET

35

Page 36: Architektura .NET oraz ASP.NET

36

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>

Page 37: Architektura .NET oraz ASP.NET

37

<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 As

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

Listing web3.config. <!-- Dla strony login_3.aspx -->

Page 38: Architektura .NET oraz ASP.NET

38

<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

Page 39: Architektura .NET oraz ASP.NET

39

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.

Page 40: Architektura .NET oraz ASP.NET

40

Usluga MS PassportServer IISPrzeglądarka

Żądanie strony która

jest uwierzytelniana

przez Passport

Czy jest cookie?

Przekierowanie

klienta do strony

logowania

Pobieranie

identyfikatora i hasła z

ekranu logowania

Farma

serwerów MS

Passport

Klient ma

Passport?

Przesyłanie danych

do Microsoftu

Dostarczanie cookie

do klienta

Udostępnianie

strony do klienta

Dostęp do strony jest

zabroniony

Dostęp przegłądarki

do uwierzytelnianej

strony

NIE

TAK

TAK

NIE

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)

Page 41: Architektura .NET oraz ASP.NET

41

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.

Page 42: Architektura .NET oraz ASP.NET

42

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.

ASP.NET

IIS

Żądanie

użytkownika

Czy jest

uwierzytelniony?

Personalizacja

włączona?

Aplikacja ASP.NET

przyjmuje tożsamość

użytkownika

Czy listy ACL

zezwalają dostęp?

Aplikacja ASP.NET

przyjmuje tożsamość IIS

Dostęp do resursu jest

dozwolony

Brak Dostępu do resursuNIE

TAK

TAK

NIE

TAK

NIE

Rys.20

Włączanie personalizacji może być zrealizowane w następnym kodzie pliku web.config:

Page 43: Architektura .NET oraz ASP.NET

43

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

Personalizacji

Dostęp anonimowy jest

dozwolony

Dostęp anonimowy jest

wzbroniony impersonate = ”true” IUSR_ComputerName Identyfikator oddalonego

użytkownika impersonate = ”false” Identyfikator procesu IIS Identyfikator procesu IIS

jest wyznaczone konto

user/psw w znacznikach <identity>...</identity

>

Wyznaczony w znacznikach <identity>...</identity

> identyfikator

Wyznaczony w znacznikach <identity>...</identity

> identyfikator

Decydującym w wyznaczeniu uprawnień przez plik web.config jest obecność parametrów

username oraz password w znacznikach <identity>...</identity> . W przypadku,

kiedy te pola maja wartości obecność pola impersonate = ”true” będzie ustalona

automatyczne.

Page 44: Architektura .NET oraz ASP.NET

44

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

Page 45: Architektura .NET oraz ASP.NET

45

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.

Page 46: Architektura .NET oraz ASP.NET

46

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.

Page 47: Architektura .NET oraz ASP.NET

47

Rys.3

Rys.4

Page 48: Architektura .NET oraz ASP.NET

48

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>

Page 49: Architektura .NET oraz ASP.NET

49

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.

Page 50: Architektura .NET oraz ASP.NET

50

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>

Page 51: Architektura .NET oraz ASP.NET

51

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" Font-

Names="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" Font-

Size="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

Page 52: Architektura .NET oraz ASP.NET

52

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

Page 53: Architektura .NET oraz ASP.NET

53

Technologia ADO Technologia ADO.NET

Przedstawienie danych:

Obiekt Recordset, przypominający

pojedynczą tabelę lub wynik kwerendy

Obiekt DataSet, który może zawierać wiele

tabel z wielu źródeł danych

Dostęp do danych:

Sekwencyjny dostęp do wierszy zapisanych

w obiekcie RecordSet.

Umożliwia całkowicie niesekwencyjny

dostęp do danych zapisanych w obiekcie

DataSet za pomocą hierarchii kolekcji

Relacje pomiędzy wieloma tabelami:

Dane z wielu tabel można połączyć w

jednym obiekcie RecordSet za pomocą

instrukcji SQL JOIN i UNION.

Do przechodzenia pomiędzy powiązanymi

tabelami służą obiekty DataRelation.

Współdzielenie danych:

Konieczne jest dokonanie konwersji typu

danych na typ akceptowany przez system-

odbiorcę, co obniża wydajność aplikacji.

Korzysta się z XML, więc konwersje typów

nie są konieczne.

Skalowalność :

Wynikiem walki o dostęp do źródła danych

jest blokowanie dostępu do bazy danych oraz

połączenia z bazą.

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 ogniowe:

Zapory mogą blokować wiele rodzajów

zapytań.

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.

Page 54: Architektura .NET oraz ASP.NET

54

DataSet

Data Table

Row Column Constraint

DataRelation

Dostawca ADO.NET

DataReader

DataAdapter

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

Page 55: Architektura .NET oraz ASP.NET

55

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()

Page 56: Architektura .NET oraz ASP.NET

56

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

Page 57: Architektura .NET oraz ASP.NET

57

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

Page 58: Architektura .NET oraz ASP.NET

58

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

Page 59: Architektura .NET oraz ASP.NET

59

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. %>

Page 60: Architektura .NET oraz ASP.NET

60

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" )

Page 61: Architektura .NET oraz ASP.NET

61

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

Page 62: Architektura .NET oraz ASP.NET

62

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.

Page 63: Architektura .NET oraz ASP.NET

63

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>")

Page 64: Architektura .NET oraz ASP.NET

64

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.

Page 65: Architektura .NET oraz ASP.NET

65

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

Page 66: Architektura .NET oraz ASP.NET

66

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

stałej

Wartość

liczbowa

Opis

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.

Page 67: Architektura .NET oraz ASP.NET

67

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.

Wersja

pierwotna

FillDataSet Wersja pierwotna

DataRowsversion.

Original

Wersja aktualna

DataRowsversion.

Currentl

Wersja

proponowana

DataRowsversion.

Proposed

Żrodło danychDataSet

Nowa Wersja

pierwotna

modyfikowanie

Tryb edytowania wiersza

Aktualizacja

Anuluj zmiany

Lub

Potwierdż zmiany

Modyfikowanie wierszy w DATASET

Rys. 22

Page 68: Architektura .NET oraz ASP.NET

68

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.

Page 69: Architektura .NET oraz ASP.NET

69

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

Page 70: Architektura .NET oraz ASP.NET

70

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.

Page 71: Architektura .NET oraz ASP.NET

71

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.

DataAdapter

DataSet

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

Page 72: Architektura .NET oraz ASP.NET

72

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">

Page 73: Architektura .NET oraz ASP.NET

73

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. "Data Source=C:\aspnet_bazy_test\banking.mdb") 10. Dim objTrans As System.Data.OleDb.OleDbTransaction

11. Dim objCmd As System.Data.OleDb.OleDbCommand = New _

12. System.Data.OleDb.OleDbCommand _

13. ("DELETE FROM tblUsers WHERE UserID=72", Conn)

14. Conn.Open()

15. objTrans = Conn.BeginTransaction()

16. objCmd.Transaction = objTrans

17. Try

18. objCmd.ExecuteNonQuery()

19. objCmd.CommandText = "INSERT INTO tblUsers " & _

20. "(FirstName, LastName, Address, City, State, " & _

21. "Zip, Phone) VALUES " & _

22. "('Jose', 'Santiago', '34 Lake Drive', " & _

23. "'Yolktown', 'MA', '02515', '8006579876')"

24. objCmd.ExecuteNonQuery()

25. objTrans.Commit()

26. Label1.Text = "Obie operacje zostaly wykonane poprawnie."

27. Catch ex As System.Data.OleDb.OleDbException

28. objTrans.Rollback()

29. Label1.Text = ex.Message & "<p>" & _

30. "Zadna operacja nie zostala wykonana."

31. Finally

32. objCmd.Connection.Close()

33. End Try

34. End Sub

35. End Class

Page 74: Architektura .NET oraz ASP.NET

74

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

Page 75: Architektura .NET oraz ASP.NET

75

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.

Page 76: Architektura .NET oraz ASP.NET

76

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

Page 77: Architektura .NET oraz ASP.NET

77

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

Page 78: Architektura .NET oraz ASP.NET

78

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.

Page 79: Architektura .NET oraz ASP.NET

79

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)

Page 80: Architektura .NET oraz ASP.NET

80

22. dadUser2.UpdateCommand = Me.cmbUser2.GetUpdateCommand

23. 'Wypelniamy zbiory danych

24. dadUser1.Fill(dstUser1, "customers")

25. dadUser2.Fill(dstUser2, "customers")

26. 'Aktualizujemy wiersz w pierwszym obiekcie DataSet

27. dstUser1.Tables("Customers").Rows(0)("City") = "Warszawa"

28. 'Aktualizujemy żrodło danych z pierwszego obiektu DataSet

29. Me.dadUser1.Update(dstUser1, "Customers")

30. 'Aktualizujemy wiersz w drugiem obiekcie DataSet

31. 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

Page 81: Architektura .NET oraz ASP.NET

81

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. Imports System.Xml 2. Namespace XML 3. Partial Class XMLReader 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 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

Page 82: Architektura .NET oraz ASP.NET

82

24. End While

25. Catch ex As Exception

26. Response.Write("Błąd dostępu do pliku XML!")

27. Finally

28. reader.close()

29. End Try

30. End Sub

31. End Class

32. 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. writer.WriteStartDocument()

11. writer.Formatting = Formatting.Indented

12. writer.Indentation = 3

13. writer.WriteStartElement("bookstore")

14. writer.WriteStartElement("book")

15. writer.WriteAttributeString("genre", "history")

16. writer.WriteAttributeString("style", "hardcover")

17. writer.WriteElementString("title", "Połtawa 1709")

18. writer.WriteStartElement("author")

19. writer.WriteElementString("first-name", _

20. "Władisław A.")

21. writer.WriteElementString("last-name", _

22. "Serczyk")

23. writer.WriteEndElement()

24. writer.WriteElementString("price", _

25. "19.99")

26. writer.WriteEndElement()

27. writer.WriteEndElement()

28. writer.Flush()

29. Catch ex As Exception

30. Response.Write("Błąd dostępu do pliku XML!")

31. Finally

32. writer.Close()

33. Response.Write("Zakończono generację pliku")

34. End Try

35. End Sub

Page 83: Architektura .NET oraz ASP.NET

83

36. End Class

37. 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. validator = New XMLValidatingReader(reader)

13. validator.ValidationType = ValidationType.XDR

14. AddHandler validator.ValidationEventHandler, New _

15. ValidationEventHandler(AddressOf ShowError)

16. While validator.Read()

17. End While

18. Catch ex As Exception

19. Response.Write("Błąd dostępu do pliku XML!")

20. Finally

21. reader.close()

22. End Try

23. End Sub

24. 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

Page 84: Architektura .NET oraz ASP.NET

84

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.

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 = ""

Plik XML Baza Danych

XmlTextreader DataAdapter

XmlDataDocument DataSet

Nnnnz Wiązanie danych Walidacja XML,

Transformacja

kodu XML,itp.

Plik XML Baza Danych

XmlTextreader DataAdapter

XmlDataDocument DataSet

Nnnnz Wiązanie danych Walidacja XML,

Transformacja

kodu XML,itp.

Page 85: Architektura .NET oraz ASP.NET

85

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

Page 86: Architektura .NET oraz ASP.NET

86

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.

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

W3C XML DOM

XML Document

XML Node

XmlTextRead

er

XmlTextWrit

er

Dane XML

Page 87: Architektura .NET oraz ASP.NET

87

33. For Each attrnode In map

34. strOutput += " <b>" & _

35. attrnode.Name & "</b> &lt;" & _

36. attrnode.Value & "&gt; " & vbCrLf

37. Next

38. End If

39. strOutput += "<br>"

40. End If

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

Page 88: Architektura .NET oraz ASP.NET

88

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"

Page 89: Architektura .NET oraz ASP.NET

89

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().

Page 90: Architektura .NET oraz ASP.NET

90

Żrodlo

danych

Kontrolka serwerowa

DataSource

Cache

Controls

DataBind()

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.

Page 91: Architektura .NET oraz ASP.NET

91

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. Imports System.Data.SqlClient

2. Imports System.Data

3. Public Class SqlRepeaterSimple

4. Inherits System.Web.UI.Page

5. #Region " Web Form Designer Generated Code "

Page 92: Architektura .NET oraz ASP.NET

92

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

Page 93: Architektura .NET oraz ASP.NET

93

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. Imports System.Data.SqlClient 2. Imports System.Data 3. Public Class SqlRepeaterFixNulls 4. Inherits System.Web.UI.Page 5. Function fixNulls(ByVal theString As String) As String 6. 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>

Page 94: Architektura .NET oraz ASP.NET

94

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. myConnection = New System.Data.SqlClient.SqlConnection _ 9. ("Server=Localhost;uid=sa;pwd=;Database=Pubs") 10. 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

Page 95: Architektura .NET oraz ASP.NET

95

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

Page 96: Architektura .NET oraz ASP.NET

96

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" SelectedItemStyle-

BackColor = "#cccc99" repeatlayout="table" repeatdirection="horizontal"

6. OnItemCommand="DataList1_ItemCommand" DataKeyField="UserID">

7. <ItemTemplate>

8. <asp:LinkButton id="button1" runat="server"

Page 97: Architektura .NET oraz ASP.NET

97

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. Imports System.Data.OleDb 2. Imports System.Data 3. Public Class wydruk0910 4. Inherits System.Web.UI.Page 5. #Region " Web Form Designer Generated Code " 6. #End Region 7. 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

Page 98: Architektura .NET oraz ASP.NET

98

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" )%>

Page 99: Architektura .NET oraz ASP.NET

99

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. Imports System.Data 2. Imports System.Data.SqlClient 3. Public Class SqlDataListEdit Inherits System.Web.UI.Page 4. #Region " Web Form Designer Generated Code " 5. #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()

Page 100: Architektura .NET oraz ASP.NET

100

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 =

Page 101: Architektura .NET oraz ASP.NET

101

73. myDataList.DataKeys.Item(e.Item.ItemIndex)

74. myConnection.Open()

75. myCommand.ExecuteNonQuery()

76. myDataList.DataBind()

77. myConnection.Close()

78. myDataList.EditItemIndex = -1

79. BindData()

80. 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.

Page 102: Architektura .NET oraz ASP.NET

102

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

Page 103: Architektura .NET oraz ASP.NET

103

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

Page 104: Architektura .NET oraz ASP.NET

104

6.Kliknij Next (Rys.29)

Rys.29

7. Ustal dostęp do bazy danych za dopomogą polecenia SQL (Rys.30).

Rys.30

Page 105: Architektura .NET oraz ASP.NET

105

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.

Page 106: Architektura .NET oraz ASP.NET

106

Rys.32

10. Kliknij Next.

Page 107: Architektura .NET oraz ASP.NET

107

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”

Page 108: Architektura .NET oraz ASP.NET

108

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.

Page 109: Architektura .NET oraz ASP.NET

109

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

Page 110: Architektura .NET oraz ASP.NET

110

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)

Page 111: Architektura .NET oraz ASP.NET

111

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.

Page 112: Architektura .NET oraz ASP.NET

112

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:

Page 113: Architektura .NET oraz ASP.NET

113

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.

Page 114: Architektura .NET oraz ASP.NET

114

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

Page 115: Architektura .NET oraz ASP.NET

115

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>

Page 116: Architektura .NET oraz ASP.NET

116

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.

Page 117: Architektura .NET oraz ASP.NET

117

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

Page 118: Architektura .NET oraz ASP.NET

118

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. ' Update Database

33. Dim myConnection As System.Data.SqlClient.SqlConnection

34. Dim myCommand As System.Data.SqlClient.SqlCommand

35. Dim sqlString As String

36. myConnection = New System.Data.SqlClient.SqlConnection _

37. ("Server=Localhost;uid=sa;pwd=;Database=Northwind")

38. sqlString = "Update Products Set ProductName = " & _

39. "@productName, UnitPrice=@unitPrice Where _

40. ProductID=@productID"

41. myCommand = New System.Data.SqlClient.SqlCommand _

42. (sqlString, myConnection)

43. myCommand.Parameters.Add ( _

44. New System.Data.SqlClient.SqlParameter("@ProductName", _

45. SqlDbType.NVarChar, 80))

46. myCommand.Parameters("@ProductName").Value = _

47. productName.Text

48. myCommand.Parameters.Add( _

49. New System.Data.SqlClient.SqlParameter("@unitprice", _

50. SqlDbType.Money))

51. myCommand.Parameters("@unitPrice").Value = _

52. CType(unitPrice.Text, Decimal)

53. myCommand.Parameters.Add( _

54. New System.Data.SqlClient.SqlParameter("@productID", _

55. SqlDbType.Int))

56. myCommand.Parameters("@productID").Value = _

57. myDataGrid.DataKeys.Item(e.Item.ItemIndex)

Page 119: Architektura .NET oraz ASP.NET

119

58. myConnection.Open()

59. myCommand.ExecuteNonQuery()

60. myConnection.Close()

61. myDataGrid.EditItemIndex = -1

62. BindData()

63. 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" )%>'

Page 120: Architektura .NET oraz ASP.NET

120

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 "

Page 121: Architektura .NET oraz ASP.NET

121

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. ' Update Database

36. Dim myConnection As System.Data.SqlClient.SqlConnection

37. Dim myCommand As System.Data.SqlClient.SqlCommand

38. Dim sqlString As String

39. myConnection = New System.Data.SqlClient.SqlConnection

40. ("Server=Localhost;uid=sa;pwd=;Database=Northwind")

41. sqlString = "Update Products Set ProductName=@productName, "_&

42. "UnitPrice=@unitPrice Where ProductID=@productID"

43. 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( _

Page 122: Architektura .NET oraz ASP.NET

122

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. #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

System.EventArgs) Handles MyBase.Load

7. If Not IsPostBack Then 8. BindData() 9. End If

Page 123: Architektura .NET oraz ASP.NET

123

10. End Sub

11. Sub BindData()

12. Dim myConnection As System.Data.SqlClient.SqlConnection

13. Dim myCommand As System.Data.SqlClient.SqlCommand

14. Dim sqlString As String

15. ' Get Records From Database

16. myConnection = New System.Data.SqlClient.SqlConnection _

17. ("Server=Localhost;uid=sa;pwd=;Database=Northwind")

18. sqlString = "Select * from Products Order By " & sortfield

19. myCommand = New System.Data.SqlClient.SqlCommand _

20. (sqlString, myConnection)

21. myConnection.Open()

22. myDataGrid.DataSource = myCommand.ExecuteReader()

23. myDataGrid.DataBind()

24. myConnection.Close()

25. 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" />

Page 124: Architektura .NET oraz ASP.NET

124

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. Sub SortGrid(ByVal s As Object, ByVal e As

28. DataGridSortCommandEventArgs)

29. sortField = e.SortExpression

30. BindData()

31. End Sub

32. 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

Page 125: Architektura .NET oraz ASP.NET

125

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

Page 126: Architektura .NET oraz ASP.NET

126

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>

Page 127: Architektura .NET oraz ASP.NET

127

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

Page 128: Architektura .NET oraz ASP.NET

128

23. If ViewState("PageNumber") = ViewState("PageCount") Then

24. nextButton.Enabled = False

25. Else

26. nextButton.Enabled = True

27. End If

28. If ViewState("PageNumber") = 1 Then

29. prevButton.Enabled = False

30. Else

31. prevButton.Enabled = True

32. End If

33. End Sub

34. Function getPageCount() As Integer

35. Dim theCount As Double

36. Dim sqlString As String

37. Dim myCommand As System.Data.SqlClient.SqlCommand

38. sqlString = "Select Count(*) theCount From Products"

39. myCommand = New System.Data.SqlClient.SqlCommand _

40. (sqlString, myConnection)

41. myConnection.Open()

42. theCount = myCommand.ExecuteScalar()

43. myConnection.Close()

44. Return Math.Ceiling(theCount / PageSize)

45. End Function

46. Sub pageGrid(ByVal s As Object, ByVal e As EventArgs)

47. If s.CommandArgument = "Next" Then

48. moveNext()

49. Else

50. movePrevious()

51. End If

52. End Sub

53. Sub moveNext()

54. Dim lastRowIndex As Integer

55. sqlString = "Select Top " & PageSize.toString() & _

56. " * from Products " _

57. & "Where ProductID > " & ViewState("endProductID")._

58. toString()

59. myAdapter = New System.Data.SqlClient.SqlDataAdapter _

60. (sqlString, myConnection)

61. myDataSet = New DataSet

62. myAdapter.Fill(myDataSet, "Products")

63. myDataGrid.DataSource = myDataSet

64. myDataGrid.DataBind()

65. ViewState("PageNumber") = ViewState("PageNumber") + 1

66. ViewState("startProductID") = _

67. MyDataSet.Tables("Products").Rows(0).Item("ProductID", _

68. DataRowVersion.Current)

69. lastRowIndex = myDataSet.Tables("Products").Rows.Count - 1

70. ViewState("endProductID") = _

71. MyDataSet.Tables("Products").Rows(lastRowIndex). _

72. Item("ProductID", DataRowVersion.Current)

73. End Sub

74. Sub movePrevious()

75. Dim myDataView As DataView

76. Dim lastRowIndex As Integer

77. sqlString = "Select Top " & PageSize.toString() & _

78. " * from Products " & " Where ProductID < " & _

79. ViewState("startProductID").toString() _

80. & " Order By ProductID DESC"

81. myAdapter = New System.Data.SqlClient.SqlDataAdapter _

Page 129: Architektura .NET oraz ASP.NET

129

82. (sqlString, myConnection)

83. myDataSet = New DataSet

84. myAdapter.Fill(myDataSet, "Products")

85. myDataView = myDataSet.Tables("Products").DefaultView

86. myDataView.Sort = "ProductID"

87. myDataGrid.DataSource = myDataView

88. myDataGrid.DataBind()

89. lastRowIndex = myDataSet.Tables("Products"). _

90. Rows.Count - 1

91. ViewState("PageNumber") = ViewState("PageNumber") - 1

92. ViewState("startProductID") = _

93. MyDataSet.Tables("Products").Rows(lastRowIndex). _

94. Item("ProductID", DataRowVersion.Current)

95. ViewState("endProductID") = _

96. MyDataSet.Tables("Products").Rows(0).Item _

97. ("ProductID", DataRowVersion.Current)

98. End Sub

99. 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

Page 130: Architektura .NET oraz ASP.NET

130

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.

Page 131: Architektura .NET oraz ASP.NET

131

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>

Page 132: Architektura .NET oraz ASP.NET

132

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; z-

index: 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;

Page 133: Architektura .NET oraz ASP.NET

133

<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.

Page 134: Architektura .NET oraz ASP.NET

134

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>

Page 135: Architektura .NET oraz ASP.NET

135

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

Page 136: Architektura .NET oraz ASP.NET

136

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.

Page 137: Architektura .NET oraz ASP.NET

137

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>

Page 138: Architektura .NET oraz ASP.NET

138

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.

Page 139: Architektura .NET oraz ASP.NET

139

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 System; 8. using System.Data; 9. using System.Configuration; 10. using System.Collections;

11. using System.Web;

12. using System.Web.Security;

13. using System.Web.UI;

14. using System.Web.UI.WebControls;

15. using System.Web.UI.WebControls.WebParts;

16. using System.Web.UI.HtmlControls;

17. public partial class _Default : System.Web.UI.Page

18. {

19. protected void Page_Load(object sender, EventArgs e)

20. {

21. }

22. protected override void OnPreInit(EventArgs e)

23. {

24. base.OnPreInit(e);

25. Theme = "red";

26. }

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 )

Page 140: Architektura .NET oraz ASP.NET

140

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">

Page 141: Architektura .NET oraz ASP.NET

141

3. <asp:Button ID="defaultButton" runat="server" Text="To jest domyślny przycisk" Width="291px" /><br />

4. <br /> 5. <asp:Button ID="flatButton" runat="server" Text="To jest płaski

przycisk" Width="290px" SkinID= "Flat" />

6. <br /> 7. <br /> 8. <asp:Button ID="inverseButton" runat="server" Text="To jest

odwroconyi przycisk" Width="290px" SkinID="Inverse" />

9. </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" ?>

Page 142: Architektura .NET oraz ASP.NET

142

<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"

Page 143: Architektura .NET oraz ASP.NET

143

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">

Page 144: Architektura .NET oraz ASP.NET

144

<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

Page 145: Architektura .NET oraz ASP.NET

145

\

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.

Page 146: Architektura .NET oraz ASP.NET

146

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.

Page 147: Architektura .NET oraz ASP.NET

147

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"

Page 148: Architektura .NET oraz ASP.NET

148

16. 'End Function

17. <WebMethod()> _

18. Public Function TestString(ByVal x As String) As String

19. TestString = _

20. "TestString received the following as input: " & x

21. End Function

22. <WebMethod()> _

23. Public Function TestMath(ByVal y As Integer, ByVal z As _

24. Integer) As Integer

25. TestMath = y + z

26. 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

Page 149: Architektura .NET oraz ASP.NET

149

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. Public Class WebForm1 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. 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)

Page 150: Architektura .NET oraz ASP.NET

150

22. ar.AsyncWaitHandle.WaitOne()

23. Me.Labelrezult.Text = "Rezultat = " + _

24. objService.EndTestMath(ar).ToString

25. '-----------koniec kodu asynchronicznego--------

26. ' synchroniczny tryb WWW serwisu (2linii):

27. ' Me.Labelrezult.Text = "Rezultat = " +_

28. ' objService.TestMath(CType(Me.TextBox2.Text, Integer), _

29. ' CType(Me.TextBox3.Text, Integer)).ToString

30. '---------koniec kodu synchronicznego----------

31. End If

32. End Sub

33. End Class

W linii 14 procedury ButtonA_Click()jest zadeklarowana zmienna obiektowa ar klasy

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 czwarty-

odwoł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

Page 151: Architektura .NET oraz ASP.NET

151

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

(header)

Pobierany zostanie wyznaczony nagłówek

open (metod,url,asynch,

username,password)

Zainicjalizowane zostanie żądanie do serwera. Pierwszy

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

(header,value)

Ustalenie wartości klucza nagłówka. (Musi być wcześniej

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, 2-

lą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. }

Page 152: Architektura .NET oraz ASP.NET

152

13. else if (window.XMLHttpRequest)

14. {

15. xmlHttp = new XMLHttpRequest ();

16. }

17. }

18. function startRequest()

19. {

20. createXMLHttpRequest();

21. xmlHttp.onreadystatechange = handleStateChange;

22. xmlHttp.open ("GET","Handler.ashx", true);

23. // xmlHttp.send(null);

24. xmlHttp.send();

25. }

26. function handleStateChange()

27. {

28. if (xmlHttp.readyState ==4)

29. {

30. if (xmlHttp.status ==200)

31. {

32. alert ("Server odpowiada: " + xmlHttp.responseText);

33. }

34. }

35. }

36. </script>

37. </head>

38. <body>

39. <input type=button value= "Startuj asynchroniczna wymiane

danych " onclick = "startRequest();"

40. />

41. </body>

42. </html>

W linii 39 jest wyznaczona funkcja klienta startRequest(), która będzie uruchomiana 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

Page 153: Architektura .NET oraz ASP.NET

153

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 „pre-

rendering”. 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>

Page 154: Architektura .NET oraz ASP.NET

154

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

Page 155: Architektura .NET oraz ASP.NET

155

10. For i = 0 To ddlModel.Items.Count - 1 Step 1

11. evArg = evArg + ddlModel.Items(i).ToString + ";"

12. Next

13. Return evArg

14. 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. scr = "<script language=javascript>"

31. 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. ddlManufacturers.Items.Add(New ListItem("Mercedes"))

52. ddlManufacturers.Items.Add(New ListItem("BMW"))

53. ddlManufacturers.Items.Add(New ListItem("Renault"))

54. ddlManufacturers.Items.Add(New ListItem("Toyota"))

55. ddlManufacturers.Items.Add(New ListItem("Daewoo"))

56. End Sub

Page 156: Architektura .NET oraz ASP.NET

156

57. Sub BindModels(ByVal manufacturer As String)

58. Select Case manufacturer

59. Case "Mercedes"

60. ddlModel.Items.Clear()

61. ddlModel.Items.Add(New ListItem("S350"))

62. ddlModel.Items.Add(New ListItem("S500"))

63. ddlModel.Items.Add(New ListItem("S600"))

64. ddlModel.Items.Add(New ListItem("CLK"))

65. 'break()

66. Case "BMW"

67. ddlModel.Items.Clear()

68. ddlModel.Items.Add(New ListItem("model 3"))

69. ddlModel.Items.Add(New ListItem("model 5"))

70. ddlModel.Items.Add(New ListItem("model 7"))

71. ddlModel.Items.Add(New ListItem("X3"))

72. ddlModel.Items.Add(New ListItem("X5"))

73. 'break()

74. Case "Renault"

75. ddlModel.Items.Clear()

76. ddlModel.Items.Add(New ListItem("12"))

77. ddlModel.Items.Add(New ListItem("19"))

78. ddlModel.Items.Add(New ListItem("21"))

79. 'break;

80. Case "Toyota"

81. ddlModel.Items.Clear()

82. ddlModel.Items.Add(New ListItem("Aristo"))

83. ddlModel.Items.Add(New ListItem("Avalon"))

84. ddlModel.Items.Add(New ListItem("Avensis"))

85. ddlModel.Items.Add(New ListItem("Bandeirante"))

86. 'break;

87. Case "Daewoo"

88. ddlModel.Items.Clear()

89. ddlModel.Items.Add(New ListItem("Sens"))

90. ddlModel.Items.Add(New ListItem("Lanos"))

91. 'break;

92. End Select

93. End Sub

94. End Class

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

Page 157: Architektura .NET oraz ASP.NET

157

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

Page 158: Architektura .NET oraz ASP.NET

158

Rys.2

Page 159: Architektura .NET oraz ASP.NET

159

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.

Page 160: Architektura .NET oraz ASP.NET

160

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" />

Page 161: Architektura .NET oraz ASP.NET

161

<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

Page 162: Architektura .NET oraz ASP.NET

162

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

Page 163: Architektura .NET oraz ASP.NET

163

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.

Page 164: Architektura .NET oraz ASP.NET

164

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:

Page 165: Architektura .NET oraz ASP.NET

165

<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

Page 166: Architektura .NET oraz ASP.NET

166

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">

Page 167: Architektura .NET oraz ASP.NET

167

<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 ?" Font-

Names="Verdana" Font-Size="8pt"></asp:Label>

</td>

</tr>

<tr>

<td style="padding-left: 20px">

<asp:RadioButtonList ID="RadioButtonList1" runat="server" Font-Names="Verdana" Font-

Size="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

Page 168: Architektura .NET oraz ASP.NET

168

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="Text-

Decoration:

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;

Page 169: Architektura .NET oraz ASP.NET

169

}

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)

Page 170: Architektura .NET oraz ASP.NET

170

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>

Page 171: Architektura .NET oraz ASP.NET

171

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)

Page 172: Architektura .NET oraz ASP.NET

172

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.

Page 173: Architektura .NET oraz ASP.NET

173

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

Page 174: Architektura .NET oraz ASP.NET

174

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ść.

Page 175: Architektura .NET oraz ASP.NET

175

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.