This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Der Verlag hat alle Sorgfalt walten lassen, um vollständige und akkurate Informationen in diesem Buch bzw. Programm und anderen evtl. beiliegenden Informationsträgern zu publizieren. SYBEX-Verlag GmbH, Düsseldorf, übernimmt weder die Garantie noch die juristische Verantwortung oder irgendeine Haftung für die Nutzung dieser Informationen, für deren Wirtschaftlichkeit oder fehlerfreie Funktion für einen bestimmten Zweck. Ferner kann der Verlag für Schäden, die auf eine Fehlfunktion von Programmen, Schaltplänen o.Ä. zurückzuführen sind, nicht haftbar gemacht werden, auch nicht für die Verletzung von Patent- und anderen Rechten Dritter, die daraus resultiert. Authorized translation from English Language Edition published by Premier Press, Inc. Original copyright ® 2002 by Premier Press, ASP.NET Professional Projects by Hersh Bhasin. Translation by SYBEX-Verlag GmbH, 2002 Übersetzer: Michael Gustmann, Andreas Hermanspann. Projektmanagement: Simone Schneider DTP: Renate Felmet-Starke, Willich Endkontrolle: Mathias Kaiser Redaktionsbüro, Düsseldorf Umschlaggestaltung: Guido Krüsselsberg, Düsseldorf Farbreproduktionen: Fischer GmbH, Willich Belichtung, Druck und buchbinderische Verarbeitung: Media Print GmbH, Paderborn
Einführung in die ASP.NET-Webformulare und Kontrollen
27
Grundlegende Techniken
28
Status-Management
29
Page-Events
30
Code Behind
32
Server-Kontrollen
37
HTML-Kontrollen
37
Web-Kontrollen
42
Inhärente Kontrollen
43
Rich-Kontrollen
54
Zusammenfassung
60
Verwendung von ADO.NET im .NET Framework
61
Das DataSet
63
Managed Provider
64
Namespace
67
Das Connection-Objekt Das DataAdapter-Objekt
67 68
Kapitel 2
Kapitel 3
Das Command-Objekt CommandType Ausführung von Befehlen
69 69 70
Aktionsanfragen mit dem Command-Objekt
72
Gespeicherte Prozeduren
76
Benutzung der Parameter-Sammlung
77
ASP.NET 6 Benutzung des Execute-Schlüsselworts zum Aufruf einer gespeicherten Prozedur Datenansichten
Kapitel 4
80
Die Grundansicht
81
Die Anwendung von Filtern in Ansichten Lesen der Zeilen und Spalten einer Datentabelle
81 83
Der DataReader
86
Beziehungen zwischen Daten
88
Zusammenfassung
91
Datenbindung
93
Bindung von Kontrollen
94
Der DataRepeater
97
DataGrid
Kapitel 5
79
100
Ein einfaches DataGrid
101
Das stilvolle Master-Grid
101
Das editierbare Masters-Formular Sortierung und Seitenwechsel
105 113
Sortierung
114
Seitenwechsel im DataGrid
115
Die Datenliste
119
Bindung an XML-Daten
125
Implementierung einer Master-Detail-Relation
126
Zusammenfassung
133
Eingabeüberprüfung
135
Ein zweigeteilter Zugang zur Validierung
137
Validierungs-Kontrollen
138
Required Field Compare Validator
141 141
RangeValidator Regular Expression
143 144
Musterfibel der regulären Ausdrücke
145
Benutzerdefinierte Überprüfung
146
Inhaltsverzeichnis 7
Kapitel 6
Kapitel 7
Serverfunktionalität
147
Funktionalität auf der Clientseite
147
Die ValidationSummary-Kontrolle
148
Die IsValid-Eigenschaft
149
Deaktivierung der clientseitigen Validierung
149
Zusammenfassung
149
Benutzer-Kontrollen
151
Erstellung einer einfachen Benutzer-Kontrolle
152
Darstellung von Eigenschaften
154
Entwurf eines Navigations-Systems für Ihre Website mit einer Benutzer-Kontrolle
156
Zusammenfassung
161
Benutzerdefinierte Kontrollen
163
Eine einfache Kontrolle in Visual Basic
165
Schritt 1: Erstellen einer klassischen Datei Schritt 2: Erstellen der DLL
165 167
Schritt 3: Erstellen des Webformulars
168
Erstellen einer einfachen Kontrolle in C#
168
Schritt 1: Erstellen einer klassischen Datei
169
Schritt 2: Erstellen der DLL Schritt 3: Erstellen des Webformulars
170 171
Die generische, benutzerdefinierte Edit/Add-Kontrolle
171
Die Config-Datei
175
Erstellen der Kontrolle Schritt 1: Der Editiermodus
179 179
Schritt 2: Der Add-Modus
182
Schritt 3: Der erste Aufbau Eigenschaften und Namensfelder
184 190
Komposition der Kontrolle
191
Der InamingContainer
194
Schritt 4: Die benutzerdefinierte GenEditAdd-Kontrolle
196
Umgang mit Events
197
ASP.NET 8 Benutzung der benutzerdefinierten GenEditAdd-Kontrolle
Kapitel 8
Kapitel 9
207
Zusammenfassung
211
Business-Objekte
213
Das bin-Verzeichnis Namensfelder und Zusammensetzungen
214 214
Ein einfaches Business-Objekt in Visual Basic
215
Eine einfache Komponente in C#
218
Aufteilung von Diensten zwischen Webformularen und Komponenten
219
Eine Datenbank-Klasse
221
Die Datenbank-Klasse in Visual Basic.NET Kompilieren der Datenbank-Klasse
221 225
Testen der Datenbank-Klasse
226
Die Datenbank-Klasse in C# Kompilieren der C#-Klasse
229 231
Testen der C#-Klasse
232
Zusammenfassung
233
Arbeiten mit ASP.NET Webdiensten
235
Erstellen eines einfachen Webservice
238
Testen des Service
239
Der WDSL-Vertrag
239
241
241 241
241
242 243
Service-Aufruf mit HTTP GET
243
Service-Aufruf mit HTTP Post
245
Service-Aufruf mit SOAP
247
Erstellen eines Webservice mit Visual Studio
252
Aufruf des Webservice aus einem Webformular
258
Inhaltsverzeichnis 9
Kapitel 10
Nutzung des Webservice-Verhaltens zur Erstellung von Funktionsaufrufen Zugriff auf Datenquellen anderer Domains
262 268
Zusammenfassung
268
ASP.NET-Anwendungen
271
Erstellung eines virtuellen Verzeichnisses Umwandeln eines bestehenden Ordners in ein virtuelles Verzeichnis
272
Erstellen eines neuen virtuellen Verzeichnisses in einem persönlichen Webserver unter Windows 2000 Die Global.asax-Datei Anwendungen oder sitzungsbezogene Objekte
273 275 280
Global.asax und Anwendungsstatus
280
Sitzungsstatus Prozessabhängiger Modus
283 286
Prozessunabhängiger Modus
287
SQL-Server-Modus
288
Cookiefreier Status
291
Die Konfigurationsdatei
Kapitel 11
273
291 292
292
293
295
297 298
300
300
Zusammenfassung
302
Caching
303
Output Caching
305
Page Data Caching
307
Datei- und Schlüsselabhängigkeit
308
Zusammenfassung
312
ASP.NET 10 Kapitel 12
Kapitel 13
Tracing
313
Page-Level-Tracing
315
Application-Level-Tracing
317
Tracing deaktivieren
318
Zusammenfassung
319
Sicherheit
321
Formularbasierte Authentifizierung
324
Ein einfaches Beispiel für eine formularbasierte Authentifizierung
324
Speicherung eines Passworts in einer Datenbank
331
Der Authentifizierungs-Provider Passport
334
Windows-basierte Authentifizierung
335
Aktivierung der Basissicherheitsauthentifizierung Konfiguration der Genehmigungen
335 336
Editieren der Web.config-Datei
337
Zusammenfassung
337
Webservices
339
Projekt 1 – Überblick
340
Kapitel 14
Aufbau eines generischen Datenbank-Webservices
343
Kapitel 15
Gestaltung eines Navigationssystems
351
Navigationslinks
352
Die Benutzersteuerung
353
Die Steuerung verwenden
354
Integration von Webservices in die Felder eines Buchhaltungsformulars
357
Kapitel 17
Einbinden eines Webservice in ein Transaktionsformular
365
Kapitel 18
Integration des Webservice in einen Saldenbilanzbericht
373
Projekt 1 – Zusammenfassung
378
Projekt 1:
Kapitel 16
Inhaltsverzeichnis 11 Projekt 2:
Visual Studio.NET Projekt 2 – Überblick
Kapitel 19
Kapitel 20
Kapitel 21
381 382
Datenbankinteraktion mit Visual Studio.NET
383
Erstellung der C#-Webanwendung
384
Der Anwendungsordner
386
Die erzeugten Dateien
386
Die Projekt-Eigenschaften (Project Properties)
388
Datenbankinteraktion
389
Erstellen einer Verbindung (Connection) und eines DataAdapters
390
Der generierte Code von SqlConnection und SqlDataAdapter
396
Das DataSet
400
CRUD-Applikationen mit Visual Studio.NET
405
Erstellen Sie eine neue C#-ASP.NET-Webanwendung
406
Die Datenkomponenten
408
DataGrid
409
Das Add-Panel
418
Die Command-Methoden des DataGrid spezifizieren
419
Methoden
420
Einen Webservice mit Visual Studio.NET erstellen
427
Den generischen Datenbank-Webservice erstellen
428
Der Webservice wird aufgebaut
430
Den Webservice aus einem Webformular aufrufen
436
Projekt 2 – Zusammenfassung
446
Stichwortverzeichnis
449
Teil 1: Die ASP.NETProgrammierumgebung
Einführung in ASP.NET
Installation des .NET Framework SDK
24
Installation der Sample-Datenbank für dieses Buch
25
ASP.NET 16
1 ASP.NET revolutioniert ASP und die damit verbundenen Datenzugangsdienste von Grund auf. Das heute als ADO.NET bekannte ADO war vielen Einschränkungen unterworfen – es war nicht strukturiert, so dass der Code nicht mit der Präsentationslogik konform war. Dieser Umstand machte es schwierig, Anwendungen zu verstehen und ihre Funktion aufrechtzuerhalten. Es war nicht möglich, die Präsentationslogik einem Webdesigner und den Code einem Programmierer zu übergeben und beide zu bitten, simultan daran zu arbeiten. Im Gegensatz zu anderen Windows-basierten Anwendungen verfügte ASP über keine inhärente Komponente oder ein Programmiermodell. ASP-Entwickler mussten eine Kombination aus Markuplanguages, Skriptumgebungen und Serverplattformen verwenden. Die Unterstützung durch Programmier-Tools war begrenzt, obwohl Visual InterDev eine Visual Basic ähnliche Benutzeroberfläche veröffentlichte, die es ermöglichte, Komponenten wie Textfenster oder Label per Drag & Drop auf ein Formular zu ziehen. Dieses Verfahren war jedoch äußerst schwerfällig und fügte den Webanwendungen große Mengen weiterer Programmiercodes hinzu, wodurch die meisten Entwickler abgeschreckt wurden. ADO, die Datenzugangskomponente von ASP, wurde entwickelt, um den Anforderungen an den Datenzugriff für Client/Server-basierte Anwendungen gerecht zu werden. Die Webprogrammierung jedoch folgte anderen Regeln. Client/Server-Anwendungen erforderten keine Optimierung von Datenbankverbindungen: Bei einer typischen Datenbankoperation wurde eine Verbindung erstellt und so lange offen gelassen, bis die Operationsschleife eines ADO-Recordset vollständig war. Datenbankverbindungen in einer Web-basierten Umgebung waren jedoch kostspielig, daher war für die Webprogrammierung eine Offline-Verbindung erforderlich. Aus dieser Notwendigkeit heraus wurde der Remote Data Service (RDS) geboren. Mit dem Aufkommen von XML (eXtensible Markup Language) wurde das Frage/Antwort-Paradigma zur Tagesordnung. Um mit den mitteilungsbasierten Kommunikationssystemen Schritt halten zu können, wurde RDS durch HTTP-Unterstützung vervollständigt, so dass auch für mittelständische Unternehmen eine Nutzung möglich wurde. XML folgt einem heterogenen, hierarchischen Datenmodell (XMLDOM), während MDAC (Microsoft Data Access Technologies) auf einem Modell verwandter Daten aufbaut. Für die Arbeit
Einführung in ASP.NET 1 mit XML mussten wir uns zwischen MSXML und MDAC entscheiden. ADO.NET jedoch löst dieses Problem. Die Unterstützung von XML beginnt bereits auf der untersten Ebene und entspricht in vieler Hinsicht der Arbeit mit Daten aus einer Datenbank. Eine Entscheidung zwischen MDAC und MSXML ist ab sofort kein Thema mehr. Webformulare, die übrigens in Kapitel 2: Einführung in die ASP.NET-Webformulare und Kontrollen beschrieben werden, sind die fundamentalen Konstruktionsbestandteile in ASP.NET. Das neu eingeführte Code Behind-Konzept ermöglicht eine Erstellung von Präsentationslogik und Skripten in getrennten Dateien. Der Zweck von Code Behind ist die Vermeidung verschachtelter Programmierzeilen und von Codes, in denen der Skriptteil mit der Präsentationslogik vermischt wird, da dies bei traditionellen ASP-Codes sehr häufig der Fall war. ASP.Net bietet ein serverbasiertes, eventabhängiges Programmiermodell ähnlich Visual Basic, wodurch die Anwendung von WYSIWYGTools wie Visual Studio ermöglicht wird. ASP.NET bietet zwei Kontrollsets, HTML und Web, die allgemein als Server-Kontrollen bekannt sind. Diese Kontrollen rendern HTML für Webseiten, halten den Status der Page beim Blättern durch die einzelnen Seiten aufrecht, stellen den Browser-Typ fest, um HTML im entsprechenden Modus rendern zu können, und dienen der Erstellung von Blöcken für zusammengesetzte Kontrollen. Diese auf dem Server vorhandenen Kontrollen senden ausschließlich HTML-Programme an den Browser. Da diese Programme von allen Browsern verstanden werden, sind sie in der Lage, die klassischen Kompatibilitätsprobleme der Browser untereinander zu umgehen. Die HTML-Kontrollen (Textfelder, Formulare, Buttons usw.) sind jene, die wir bereits benutzen, mit dem Unterschied, dass ihnen ein neues "runat = server"-Attribut hinzugefügt wurde. Die Kontrollen dienen einzig dazu, einen schnellen Pfad zu ASP.NET zu finden, da jede HTML-Kontrolle durch Hinzufügen des Attributs "runat = server" in eine ASP.NET-Kontrolle konvertiert werden kann. Web-Kontrollen beinhalten ein hohes Maß an Abstraktion sowie Nutzbarkeit. Man unterscheidet zwischen vier verschiedenen Web-Kontrollen: Inhärente Kontrollen, Rich-Kontrollen, Listen-Kontrollen (List Controls) und Gültigkeits-Kontrollen (Validation Controls). Inhärente Kontrollen bestehen aus Textfeldern, Buttons und DropDown-
17
ASP.NET 18
1 Menüs. Diese Kontrollen verfügen über ein spezielles ASP.NETVorzeichen (Tag), das sie von normalen HTML-Kontrollen unterscheidet, sowie über ein "runat = server"-Attribut. Ein Textfeld wird daher folgendermaßen erstellt:
Der Zweck von Web-Kontrollen ist die Vereinfachung der Nomenklatur der Kontrollen. Mehrere Kontrollen, die sich in ihrer Funktionalität überschneiden, wurden zu einer einzigen Kontrolle zusammengefasst. Eigenschaften wie etwa ForeColor, Font, BackColor, Enabled bleiben jedoch bestehen. Der Entwickler muss sich daher lediglich eine Eigenschaft merken, die er auf alle Kontrollen anwenden kann. Rich Controls bestehen aus Calendar und AdRotator. Der Calendar sendet HTML-Codes an einfachere Browser (Browser, die kein DHTML unterstützen) und DHTML an höher entwickelte Browser. Der AdRotator zeigt wechselnde Werbeeinblendungen an. Listen-Kontrollen werden in Kapitel 4 Datenbindung behandelt. Diese Kategorie enthält drei verschiedene Kontrollen: DataGrid, DataList und DataRepeater. Diese Kontrollen automatisieren die Anzeige von Daten aus Datenbanken. Der Entwickler fügt diesen Kontrollen eine Reihe von Schablonen (Templates) hinzu, um sie möglichst weitgehend anzupassen. DataGrid kann sogar zur Veränderung von Daten in der Anwendung verwendet werden. Gültigkeits-Kontrollen werden in Kapitel 5 Eingabeüberprüfung beschrieben. Sie automatisieren die tatsächliche Eingabe eines Autorisierungscodes. Es gibt fünf Kontrollen für die Eingabeüberprüfung und eine zusammenfassende Kontrolle. Die Gültigkeits-Kontrollen sind: RequiredFieldValidator, RegularExpressionValidator, CompareValidator, RangeValidator, CustomValidator und ValidationSummary. Die Aufgabe der einzelnen Kontrollen ergibt sich aus der Bezeichnung. So verbietet zum Beispiel der RequiredFieldValidator dem Benutzer, ein bestimmtes Texteingabefeld leer zu lassen. Desgleichen überprüft der RangeValidator, ob die Eingaben des Benutzers in einen bestimmten Bereich fallen. Die Einbindung solcher Überprüfungen in ein ASP.NET-Webformular ist eine einfach zu lösende Aufgabe. Sie müssen lediglich ein Texteingabefeld mit der passenden Gültigkeits-Kontrolle integrieren.
Einführung in ASP.NET 1 ADO.NET, der jüngste Spross der ADO-Familie, wird in Kapitel 3 Benutzung von ADO.NET im NET-Framework beschrieben. Mit der Einführung von ADO.NET hat Recordset ausgedient. Wie bereits erwähnt, konnte Recordset nur die abhängige Behandlung von Datenbankinformationen durchführen. Mit dem Aufkommen von XML und seinem heterogenen, hierarchischen Datenmodell waren die Tage von Recordset gezählt. Mit ASP.NET wurde ein neues Verfahren namens DataSet eingeführt. DataSet ist eine komplett gespeicherte Kopie einer Datenbank mit Tabellen, Spalten, Relationen, Begrenzungen und Daten. Es ermöglicht Relationen zwischen verschiedenen Tabellen analog zu einer fremden Hauptrelation innerhalb der Datenbank. Die Relationen ermöglichen es Ihnen, durch die verschiedenen Tabellen zu browsen. DataSet verfügt über einige herausragende Eigenschaften. Es kann mit zahlreichen Datenquellen kommunizieren, Daten von einer Datenbank, einer XML-Datei, einem Code oder der Eingabe eines Benutzers empfangen. Ungeachtet der Herkunft der Daten werden diese durch den immer gleichen Satz von Standard-APIs verarbeitet. Das DataSet wird in den XML-Datenstrom integriert und kann so ohne durch Firewall-Programme verursachte Interferenzen übertragen werden. Die Übertragung eines ADO-Recordset ohne Verbindung war nur durch COM-Marshalling möglich. Benutzer-Kontrollen, wie sie in Kapitel 6 Benutzer-Kontrollen beschrieben werden, sind eine Weiterentwicklung der serverseitigen "include files". Include Files sind statische Dateien. Benutzer-Kontrollen hingegen bieten eine objektorientierte Unterstützung, die es Ihnen ermöglicht, eigene Eigenschaften und Benutzermethoden zu programmieren. Diese Kontrollen funktionieren ähnlich wie die tatsächlichen ASPKontrollen, da sie Methoden und Eigenschaften sichtbar machen. In Kapitel 6 Benutzer-Kontrollen wird an einem Beispiel die Erstellung einer Benutzer-Kontrolle zur Automatisierung von Website-Navigationslinks zu den in einer XML-Datei spezifizierten URLs erklärt. ASP.NET bietet einen sehr sauberen und eleganten Zugang zur Erstellung benutzerdefinierter Kontrollen. Dieser Prozess wird in Kapitel 7 Benutzerdefinierte Kontrollen detailliert beschrieben. Des Weiteren wird die Erstellung einer Komponente (hier GenEditAdd) zur Erweiterung der DataGrid-Funktionalität (DataGrid = Datengitter) erklärt. Mit der GenEditAdd-Komponen-
19
ASP.NET 20
1 te können Sie die Werteeingabe automatisieren und zum Editieren der DataGrid-Funktionalität verwenden, wobei hier eine Anzahl von Events programmiert werden muss. Die GenEditAdd-Komponente erfordert lediglich einfache Einstellungen der Eigenschaften; die Generierung des Codes erfolgt automatisch. Die Implementierung von Business Logic war schon immer ein wichtiger Bestandteil von Web- und Server/Client-Anwendungen. ASP.NET hat den Prozess der Komponentenregistrierung stark vereinfacht. Wenn Sie in der Vergangenheit bereits COM-Objekte entwickelt haben, wissen Sie, wie aufwändig die Prozedur der Komponentenregistrierung ist. Diese Registrierung war nur mit Hilfe der regsvr32-Utility möglich. Im Falle einer Modifizierung der Komponente musste der gesamte Webserver gestoppt werden, um diese neu zu registrieren. Dieser Prozess wurde deshalb scherzhaft DLL-Hell (DLL-Hölle) genannt. In ASP.NET werden die Komponenten einfach kopiert und in das bin-Verzeichnis eingefügt. Ein Update der Registrierung ist nicht erforderlich. Detaillierte Informationen zu diesem wichtigen Thema finden Sie in Kapitel 8 Business-Objekte Webservices sind die Hauptprotagonist der .NET-Arena. Der Inhalt dieses Buches und der Projekte gibt diese Bedeutung wieder. Ein Webservice ist eine Kombination aus drei Bestandteilen: Einer in einer .NET-kompatiblen Sprache geschriebene Funktion, SOAP und XML. Wenn Sie einen bestimmten logischen Vorgang mehrmals wiederholen müssen, sollten Sie diesen als Funktion programmieren. Sie können eine Sammlung von Funktionen, die einem gemeinsamen Zweck dienen, in einem Business-Objekt zusammenfassen. So bestehen zum Beipiel die vier wichtigsten Datenbankoperationen aus insert, delete, update und select (Einfügen, Löschen, Aktualisieren, Auswählen). Sie können eine generische Funktion für jede Operation schreiben und diese in einem BusinessObjekt (beispielsweise mit dem Namen "DataBaseClass") zusammenfassen. Diese kann nun mitsamt ihren Funktionen initiiert und von jedem Objekt, das diese Funktionalität benötigt, aufgerufen werden. Ein Webservice ist ein Web-fähiges Business-Objekt, also eine Sammlung von Funktionen, die über das Web aufgerufen werden können. Für einen Webservice werden normale Funktionen programmiert, mit dem einzigen Unterschied, dass diese Funktionen mit einer speziellen
Einführung in ASP.NET 1 Markierung versehen sind, die sie als Webservice identifiziert. Ein Standard namens SOAP (Simple Object Access Protocol) legt bestimmte Regeln fest, die von der Maschine, die eine Funktion aufruft, sowie der Maschine, die als Antwort eine Sammlung von Ergebnissen zurücksendet, befolgt werden müssen. Frage und Antwort müssen in XML erfolgen. Das hieraus entstehende XML-Dokument folgt den Regeln des SOAP-Standards. Der Austausch von Informationen in XML ermöglicht es einer Clientanwendung, eine Serverfunktion aufzurufen, ungeachtet des jeweiligen Betriebssystems, der verwendeten Programmiersprache und der unterstützten Komponenten. Das liegt daran, dass XML eine Sprache ist, die von allen Maschinen verstanden wird, da SOAP HTTP verwendet, das am weitesten verbreitete und von fast allen Servern benutzte Datenübertragungsprotokoll des Internets. Kapitel 9 Arbeiten mit ASP.NET-Webdiensten bietet detaillierte Informationen zu diesem Thema. In Kapitel 10 ASP.NET-Anwendungen werden ASP.NET-Anwendungen erklärt. Eine ASP.NET Anwendung besteht aus einem virtuellen IIS-Ordner und dessen Unterordnern. Alle Webanwendungsdateien werden in diesen Ordner kopiert. Dieser Ordner hat ein spezielles Unterverzeichnis mit der Bezeichnung bin, in dem sich alle kompilierten Business-Objekte und Webdienste befinden. Wenn Sie eine neue Komponente registrieren wollen, müssen Sie lediglich die DLL-Datei kopieren und in diesen Ordner einfügen (im Gegensatz zur Verwendung von regsvr32). Der Ordner enthält zusätzlich zwei spezielle Dateien: web.config und global.asax. web.config ist eine Datei, die Sie zur Konfiguration bestimmter Facetten der Anwendung verwenden werden (z.B. Einstellung von Sicherheit, Cache und Tracing). Die global.asax-Datei enthält Programmdirektiven bezüglich des Anwendungs-Levels und Events der Sitzungsart (session) sowie eine Deklaration der Objekte, die allen Teilen der Anwendung zugänglich sind. Im Allgemeinen kann man sagen, dass diese Datei die Funktionalität der in ASP enthaltenen global.asa Datei verbessert. In Kapitel 11 Caching wird der Vorgang der Cachespeicherung erklärt. Hierbei geht es darum, dass der Benutzer kurz vorher besuchte Websites in Erinnerung behält. Die Konstruktion einiger Komponenten einer Website kann sehr kostspielig sein. Daher sollten diese Komponenten für einen bestimmten Zeit-
21
ASP.NET 22
1 raum oder bis zur Durchführung eventueller Änderungen gespeichert werden. In diesem Fall wird bei einer Anfrage an diese Ressource dieselbe nicht neu erstellt, sondern einfach nur aus dem Cachespeicher heraus aufgerufen. Normalerweise bestehen diese Komponenten aus Fuktionen, die über einen längeren Zeitraum hinweg unverändert bleiben, wie etwa Einkaufs- oder Preislisten. Informationen zum Tracing finden Sie in Kapitel 12 Tracing. Entwickler haben sich häufig darauf verlegt, eine Anzahl von "Response.Write()"-Statements in ihren Code zu integrieren, um fehlerhafte Programmzeilen zu finden (debug). Wenn ein Problem lokalisiert wurde, müssen die FehlerbehebungsStatements (debugging statements) entfernt werden. Diese Methode ist umständlich und häufig eine Quelle weiterer Fehler, weil bei dieser Prozedur möglicherweise aus Versehen Programmierzeilen mitgelöscht wurden. ASP.NET beinhaltet eine elegante Methode zur Erstellung von Fehlerbehebungs-Codes (Debug-Codes). Das Debugging wird durch Hinzufügen einer Page-Level-Anweisung (oder durch Aktivierung in der web.config-Datei) ermöglicht. Debug-Statements werden unter Verwendung von Trace.write()geschrieben. Wenn alle Fehler aus dem Formular entfernt wurden, müssen die Statements nicht extra aus dem Code entfernt werden. Durch Deaktivierung der Trace-Funktion werden die Fehlermeldungen im Browser nicht mehr angezeigt. Das Thema Sicherheit wird in Kapitel 13 behandelt. ASP.NET implementiert die Authentifizierung durch AuthentifizierungsProvider. Diese Provider bestehen aus Modulen mit einem Code, der die Eingaben des Benutzers überprüft. Derzeit sind drei Authentifizierungs-Provider verfügbar: Windows-Authentifizierung, Passport-Authentifizierung und Cookie-Authentifizierung. Das Kapitel enthält Informationen zu allen drei Providern. Das Internet erweitert die traditionellen Arten der Anwendungserstellung um einige interessante Möglichkeiten. Die verschiedenen Module einer Buchhaltungsanwendung müssen nicht länger verdrahtet werden. Mit Hilfe von Webdiensten und ASP.NET lassen sich Anwendungen entwickeln, mit denen sich Daten durch Internet und HTTP empfangen und versenden lassen. In Projekt 1 (welches sich über 5 Kapitel erstreckt) wird der Aufbau eines generischen Datenbankzu-
Einführung in ASP.NET 1 gangsdienstes erklärt, der dazu verwendet werden kann, mit anderen Datenbanken zu interagieren. Die Funktionalität dieses Dienstes beinhaltet die Möglichkeit der Eingabe, Aktualisierung, Löschung und Auswahl von Aufzeichnungen der Datenbank. Der Service akzeptiert Datenbankverbindungen und SQL-Anfragen als Parameter. Wenn es sich dabei um eine Aktionsanfrage handelt (Eingeben, Aktualisieren oder Löschen), wird die entsprechende Aktion ausgelöst. Handelt es sich um eine Suchanfrage (bei der eine Sammlung von Ergebnissen zurückgegeben wird), wird dem Objekt, von dem die Anfrage ausging, eine Sammlung von Daten zurückgesandt. Dieses DataSet kann zur Bündelung eines DataGrid verwendet werden. Veranschaulicht wird dieser Dienst durch Einbindung in den persönlichen Finanzmanager, den Sie in Projekt 1 entwickelt haben. Das Projekt demonstriert auch die Verwendung der Navigations-Kontrolle für Benutzer, deren Erstellung in Kapitel 6 Benutzer-Kontrollen erklärt wird. Die Navigations-Kontrolle bildet die Site-Navigation der Anwendung unter Verwendung von in einer XML-Datei definierten URLs. Die Vorteile eines von der Hauptanwendung getrennten Navigationssystems bestehen darin, dass Links hinzugefügt oder gelöscht werden können, ohne dass hierfür die Webseiten innerhalb der Anwendung geändert werden müssen. In Projekt 2 (Kapitel 19-21) werden einige wichtige Merkmale von Visual Studio beschrieben. Kapitel 20 CRUD-Applikationen mit Visual Studio.NET enthält eine Übersicht der wichtigen Optionen von Visual Studio.Net sowie der verschiedenen verfügbaren Assistenten, Tools und Komponenten. Des Weiteren werden Sie lernen, das typisierte DataSet zur Anzeige von Datenbankinformationen mit Hilfe der Drag & Drop-Funktionen von Visual Studio.NET einzusetzen. Visual Studio.NET hilft Ihnen, mit Datenbanken zu interagieren und Datensätze hinzuzufügen, zu löschen oder zu aktualisieren. Sie werden herausfinden, wie Sie das DataGrid mit Hilfe von Paging und Sorting innerhalb des Visual Studio.NET verändern können. In Kapitel 21 Einen Webservice mit Visual Studio.NET erstellen schließlich werden Sie lernen, Webdienste mit Visual Studio.Net zu entwickeln und zu benutzen.
23
ASP.NET 24
1
Installation des .NET Framework SDK Das .NET SDK kann von der Microsoft Download Site http://msdn.microsoft.com/net/ heruntergeladen werden. Der Download ist ziemlich groß, so dass Sie eventuell erwägen sollten, die Datei als CD zu bestellen. Das Programm ist als Standard- und als Premium-Version erhältlich. Die Premium-Version bietet zusätzliche Funktionen, wie etwa Output-Cache, Web Farm Session State, Code Access Hosting und Unterstützung für vier und mehr Prozessoren. Die einfache Installation erfolgt durch Initiierung der Datei setup.exe. Falls erforderlich, sollten Sie die Windows-Installationskomponenten aktualisieren sowie die aktuellen Patches für Ihre Windows-Version installieren. Aktualisieren Sie ebenfalls Ihre MDAC (Microsoft Data Access Components) auf die neueste Version (derzeit Version 2.7). Wenn Sie das Installationsprogramm darauf hinweist, dass ADO 2.7 nicht installiert ist, können Sie trotzdem durch Ignorieren der Meldung mit der Installation fortfahren. Sie haben die Möglichkeit, SDK-Samples zu installieren. SDK-Samples sind eine reichhaltige Informationsquelle; Sie sollten diese Option daher auswählen. Die Microsoft Data Engine (MSDE), die mit den Samples installiert wird, enthält die entsprechenden Datenbanken. Eine begrenzt gültige Kopie des Microsoft SQL Servers erhalten Sie unter http://www.microsoft.com/sql/evaluation/trial/ 2000/default.asp. Sie können auch eine CD-Version des Servers bestellen. Hierfür fallen lediglich die Portokosten an. Sobald Ihr SQL Server installiert ist und läuft, installieren Sie die ASP.NET-QuickStart-Samples. Die Samples sind eine exzellente Übungsquelle für ASP.NET. Um die Samples zu installieren, öffnen Sie den Link Microsoft NET Framework SDK/Samples and QuickStart Tutorials, der den Programmen während der SDK-Installation hinzugefügt wird, und folgen den Installationsanweisungen. Sobald die Samples installiert wurden, können sie über http://localhost/quickstart/default .aspx aufgerufen werden. Nach der SDK-Installation benötigen Sie zum Schreiben von Skripten lediglich einen Text-Editor. Sie können auch das Visual Studio CD-Set (gegen eine geringe Gebühr) bestellen und
Einführung in ASP.NET 1 dieses zur Entwicklung Ihrer Skripte benutzen. Wenn Sie Visual Studio bereits besitzen, finden Sie Framework SDK auf der zweiten CD-ROM. Sofern angebracht, wird in diesem Buch an bestimmten Stellen auch die Erstellung mit Visual Studio erklärt. Das gesamte Projekt 2 ist der Arbeit mit Visual Studio gewidmet. Dieses Thema wurde an das Ende dieses Buches gesetzt, um Ihnen die Gelegenheit zu geben, sich vor der Verwendung der Visual Studio IDE-Tools und Assistenten mit ASP.NET vertraut zu machen, da hierfür Kenntnisse der Codeprogrammierung erforderlich sind. Als Text-Editor ist TextPad besonders empfehlenswert. Dieses Shareware-Programm ist unter http://www.textpad.com erhältlich. Hier können Sie auch die Syntaxdefinitions-Datei für .NET herunterladen. Diese Datei dient dazu, verschiedene ASP.NET-Schlüsselwörter in unterschiedlichen Farben darzustellen.
Installation der Sample-Datenbank für dieses Buch Für die in diesem Buch behandelten Projekte und Samples wird eine Sample-Datenbank verwendet, die auf der CD des Buches verfügbar ist. Es ist sehr wichtig, dass Sie diese Datenbank gleich zu Anfang installieren. Suchen Sie hierzu die Datei create sql, die sich im »sample\database-Ordner befindet, und öffnen Sie diese in ISQL (SQL Query Analyzer Tool). Hierdurch werden eine Reihe von Tabellen, gespeicherten Prozeduren und Trigger erstellt. Details zu den erstellten Datenbankobjekten finden Sie in Anhang A Installation der Sample-Datenbank.
25
Einführung in die ASP.NETWebformulare und Kontrollen
Grundlegende Techniken
28
Status-Management
29
Page-Events
30
Code Behind
32
Server-Kontrollen
37
HTML-Kontrollen
37
Web-Kontrollen
42
Inhärente Kontrollen
43
Rich-Kontrollen
54
Zusammenfassung
60
ASP.NET 28
2 ASP.NET-Formulare wurden entwickelt, um eine ganze Reihe von Unzulänglichkeiten in ASP-Seiten zu beseitigen. Die HTML-Elemente und Skriptcodes dieser Seiten sind im Allgemeinen notwendigerweise sehr verschlungen, wodurch die daraus entstehende Seite unordentlich wirkt. Es ist nicht einfach, diese Seiten mit WYSIWYG-Tools zu editieren. ASP.NET verbessert die Seiten und fügt einige interessante Erweiterungen hinzu. Es bietet ein server- und eventbasiertes Programmierungsmodell ähnlich Visual Basic. Durch die hiermit eingeführte Code Behind-Technologie hat der Entwickler die Möglichkeit, den Skriptcode separat zur HTML-Datei zu speichern. ASP.NET führt zwei Arten von Kontrollen ein: HTMLKontrollen und Web-Kontrollen, die gemeinhin als ServerKontrollen bekannt sind. Diese Kontrollen rendern HTML für Webbrowser und halten den Status bei Rundläufen durch Feststellung des Browser-Typs aufrecht. Ein Rundlauf tritt immer dann auf, wenn der Benutzer ein Formular ausfüllt oder wenn ein Event eintritt, durch das eine Nachricht an den Server geschickt wird, zum Beispiel, wenn der Benutzer ein Textfeld oder ein Formular ausfüllt und dann auf die Senden-Taste klickt. Der Server verarbeitet die eingehenden Informationen und sendet die Seite zurück an den Client, wo diese dann angezeigt wird. Der ursprüngliche Zustand des Formulars wird hierbei durch ASP.NET beibehalten. Wenn also der Benutzer ein Formular ausfüllt und an den Server sendet, behält das Textfeld die eingegebenen Informationen, auch wenn zwischendurch eine andere Seite angezeigt wurde. Dies ist eine höchst nützliche Veränderung gegenüber der traditionellen ASP-Programmierung, bei der der Entwickler sich um die Beibehaltung des Status kümmern musste, da die Eingaben des Benutzers nach dem Versand des Formulars verloren gingen.
Grundlegende Techniken Zur Erstellung eines ASP.NET-Formulars müssen Sie lediglich einen Text oder eine HTML-Datei mit der .aspx-Erweiterung abspeichern. Sie benötigen keine speziellen Tools; es genügt ein einfacher Editor wie etwa Notepad. Sie können auch Visual Studio.NET, eine schnelle Benutzerumgebung zur Entwicklung von Anwendungen, verwenden, die es Ihnen ermöglicht, die benötigten Kontrollen per Drag & Drop in das Formular zu ziehen.
Einführung in die ASP.NET-Webformulare 2 Darüber hinaus bieten ASP.NET-Formulare auch eine selektive Abwärtskompatibilität. So können Sie zum Beispiel einen normalen ASP-Code nehmen und Skriptvorzeichen (Tags) mit HTML-Elementen vermischen, indem Sie die durch gekennzeichneten Blöcke verwenden. ASP- und ASP.NET-Anwendungen können auf dem IIS ohne Probleme nebeneinander laufen. Jedoch müssen ASP-Anwendungen, die mit Hilfe der Visual Basic Scripting Edition entwickelt wurden, modifiziert werden, um mit ASP.NET kompatibel sein zu können. In ASP.NET werden Skriptblöcke kompiliert und nicht interpretiert, wodurch die Leistungsfähigkeit verbessert wird. Die Kompilierung des Codes umfasst auch die Konvertierung der Codeanweisungen in die Maschinensprache. In ASP.NET wird der Code jedoch nicht direkt in eine Maschinensprache, sondern zunächst in eine Zwischensprache namens Microsoft Intermediate Language (MSIL oder IL) kompiliert. Erst der IL-Code wird dann mit Hilfe des JIT-Compilers (Just-in-time-Compiler) in die Maschinensprache übertragen. Der JIT-Compiler kompiliert jeden Teil des Codes in dem Moment, in dem dieser aufgerufen wird, anstatt die gesamte Anwendung auf einmal zu bearbeiten. Dies führt zu schnelleren Ausführungszeiten. Der kompilierte Code wird gespeichert, bis die Anwendung beendet wird, und muss daher nicht jedes Mal bei Aufruf einer Codezeile neu kompiliert werden. Diese Verfahren ermöglicht es, dass der IL-Code fast genauso schnell ausgeführt wird wie ein Maschinencode.
Status-Management Sie können die Skriptblöcke zwar verwenden, erhalten dadurch aber keine saubere Programmierumgebung. Legen Sie die Grundvoraussetzung für die Beibehaltung des Status in einem Rücksendeformular an. Dieses Formular akzeptiert die Eingaben des Benutzers und sendet sie an die eigene Adresse zurück. Die eingegebenen Werte müssen gespeichert werden, so dass dem Benutzer, wenn er bei der Eingabe einen Fehler macht, die vorher eingegebenen Werte angezeigt werden, damit er diese korrigieren kann. Die Programmierung eines solchen Formulars in der ASP-Umgebung schließt das Response-Objekt zur Extrahierung der
29
ASP.NET 30
2 Werte und einen -Block zur Anzeige der übertragenen Werte ein. Hier ein Beispiel: Listing 2.1: State.asp Name:
In ASP.NET ist das Statusmanagement (State management) automatisch bei der Benutzung von Server-Kontrollen innerhalb einer Formular-Kontrolle auf folgende Weise aktiviert: Listing 2.2: State.aspx Name:
Beachten Sie, dass das Formular sich automatisch an die eingegebenen Werte erinnert. Mit der Benutzung von Server-Kontrollen für das Statusmanagement sind aber auch einige wenige Nachteile verbunden: Sie können lediglich die SendeMethode verwenden und nur ein Formular in Ihre Website einfügen.
Page-Events ASP.NET ist von Grund auf objektorientiert. Sie können verschiedene Events in einer Visual Basic-ähnlichen Art programmieren. Wenn das Formular geladen wird, startet auch das Page_Load-Event; die Formular-Kontrollen sind verfügbar. Wenn der Benutzer mit dem Formular interagiert, werden weitere Events generiert. Das Form Unload-Event tritt ein, wenn
Einführung in die ASP.NET-Webformulare 2 die Seite verlassen wird. Dank dieser eventbasierten Struktur kann der Entwickler für Webanwendungen endlich eventbasierte Programmiertechniken einsetzen. Abbildung 2.1 zeigt Ihnen, wie vom Benutzer eingegebene Werte akzeptiert werden, um eine Kalkulation mit Hilfe dieser Techniken durchzuführen. Listing 2.3: Event.aspx >%@ Page Language="vb" %> Sub Calculate (src As Object, e As EventArgs) Amount.Text = Cstr(cint(qty.text)*cint(price.text)) End sub Page Events Qty: Price: Amount:
Oben auf der Seite wird durch @ Page_Language festgelegt, dass Visual Basic als Skriptsprache verwendet werden soll. Jedem Objekt kann eine id-Eigenschaft zugewiesen werden. Hierdurch können die Eigenschaftswerte des Objekts festgelegt werden. In diesem Fall greift man auf die Texteigenschaften für das Price- und das Qty-Fenster anstatt auf die übertragenen Daten durch das Response-Objekt zu. Setzen Sie Ihren Code in das OnClick-Event des Buttons und verwenden Sie so die neuen eventbasierten Eigenschaften von ASP.NET. Beachten Sie, dass Sie dadurch in der Lage sind, die ReadOnlyEigenschaft des Amount-Textfeldes festzulegen, indem Sie seinen Eigenschaftswert auf "True" setzen.
31
ASP.NET 32
2
Page Events Abb. 2.1
Code Behind Wie bereits erwähnt, besteht eine der größten Einschränkung von ASP in der Art, wie der Skriptcode mit den HTML-Zeichen vermischt ist. Hierdurch wird die Trennung des Inhalts von der Präsentation erschwert. Es wird schwierig, die Seite zu warten, und in Firmen, in denen Entwickler und Designer zusammenarbeiten, ist eine Trennung der Aufgabenbereiche unmöglich. Code Behind ist eine Technik, die es ermöglicht, Inhalt und Skript zu trennen. Formulare können durch Skriptcodes und HTML-Markierungen in Unordnung gebracht werden. Um diese Unordnung zu reduzieren, können Sie den gesamten Skriptcode aus einem Webformular nehmen und in einer separaten Datei speichern. Wenn Sie mit einem Visual Basic-Code arbeiten, wird diese Datei die Erweiterung .vb haben. Bei C# besteht die Erweiterung aus .cs. Als Erstes müssen Sie in einer Code-Behind-Datei die Namensfelder importieren. Namensfelder (Namespaces) sind eine Art umfassende Referenz in einem Visual Basic-Projekt. Wenn Sie sich in Visual Basic auf eine DLL beziehen, können Sie die darin enthaltenen Methoden verwenden. Desgleichen können Sie durch den Import des Namensfeldes (Namespace) die gesamte darin enthaltene Funktionaliät in Ihrem Webformular nutzen.
Einführung in die ASP.NET-Webformulare 2 Wenn Sie diese Deklaration nicht nutzen, müssen Sie einen vollständigen Pfad angeben, um eine Methode in einem bestimmten Namensfeld nutzen zu können. Ein solcher Pfad kann sehr lang (und daher umständlich einzugeben) sein. Durch die Import-Direktive können Sie direkt durch Angabe des Namens auf eine Methode zugreifen. Hier einige häufig benutzte Namensfelder (Namespaces): System: Enthält einige fundamentale Klassen und Datenbankklassen, die häufig benutzte Werte, Referenzdatentypen, Events, Operatoren, Benutzeroberflächen, Attribute und einige Verarbeitungsausnahmen definieren. System Collection: Enthält Klassen, die Listen, Reihen, Arrays, Hashtables und Wörterbücher enthalten. System.Web.UI.Control: Der Urvater aller Kontrollen von Webformularen. Enthält drei häufig verwendete Kontrollen: Page, UserControl und LiteralControl. Jede ASP.NET-Seite wird durch das ASP.NET-Framework in eine Page Control kompiliert. System.Web.UI.WebControl: Enthält Klassen, die die ASP.NET-Server-Kontrollen definieren. System.Web.UI.HTMLControls: Die hierin enthaltenen Klassen definieren die HTML-Kontrollen. Namensfelder wie System.Data, System.Data.OleDb, System.Data.SqlClient und System.XML manipulieren Datenbanken, XML und andere Daten. Diese Namensfelder werden in Kapitel 3 Verwendung von ADO.NET im .NET-Framework erklärt.
HINWEIS Imports wird von Visual Basic verwendet. Wenn Sie C# benutzen, ersetzen Sie Imports durch Using.
Wenn Sie Web-Kontrollen in Ihr .aspx-Formular integriert haben und sich in Ihrer Code Behind-Datei auf diese beziehen wollen, wird die Import-Funktion folgendermaßen aussehen:
33
ASP.NET 34
2 Imports Imports Imports Imports
System System.Collections System.Web.UI System.Web.UI.WebControls
Definieren Sie nun eine Klasse. Alle Ihre Funktionen und Unterfunktionen gelangen in diese Klasse. Eine mit Visual Basic erstellte Code Behind-Datei könnte etwa so aussehen: Imports System.Data Public Class BaseClass Inherits System.Web.UI.Page Sub somesub() End Sub Function somefunction() End Function
Beachten Sie, dass hierbei das Schlüsselwort "Inherits" eingeführt wird. Der Unterschied zu dem Schlüsselwort "Imports" besteht darin, dass Imports zwar eine Reihe von Funktionen integriert, diese aber nicht nutzt. Diese Vorgang ist vergleichbar mit der Einbindung einer Referenz in Visual Basic. Das "Inherits"-Schlüsselwort ist weitaus dynamischer. Ein Objekt, das eine gewisse Funktionalität übernimmt, kann sogar die Hauptfunktionalität übersteuern und/oder erweitern. Das Formular wird hierdurch zu einer Grundlage, aus der Ihre aspx-Page hervorgeht. Dieser Vorgang lässt sich ganz einfach bewerkstelligen. Sie müssen lediglich ein Statement an den Anfang Ihres aspx-Formulars stellen:
Schauen Sie sich das folgende Beispiel an. Hier wird eine Event.aspx-Datei in zwei Dateien geteilt: events_cb.aspx und events_cb.vb, die Code-Behind-Datei. Listing 2.4: Events_cb.aspx
Einführung in die ASP.NET-Webformulare 2 Qty: Price: Amount: Listing 2.5: Events_cb.vb Imports System Imports System.Collections Imports System.Web.UI Imports System.Web.UI.WebControls Public Class BaseClass Inherits System.Web.UI.Page 'Jede in events.aspx verwendete Kontrolle wird hier durch dieselbe id deklariert Protected qty as textbox Protected price as textbox Protected amount as textbox Sub Page_Load(Source As Object, E as EventArgs) 'Mit diesem Event wird die Seite geladen 'Wird bei jedem Laden der Seite gestartet response.write("Page Load Event : -->fired ") if NOT (isPostBack) 'Der Code wird nur einmal bei jedem Laden der Seite gestartet 'Bei wiederholtem Aufrufen der Seite wird diese Funktion auf Grund der isPostBack-Konstruktion nicht mehr ausgelöst response.write("The not isPostBack construct:-->ensures this does not get fired at reloads") end if End Sub 'Sub wird aus dem evens.aspx-Formular verschoben Sub Calculate(src As Object,e as EventArgs) Amount.Text = Cstr(cint(qty.text)*cint(price.text)) End Sub End Class
35
ASP.NET 36
2 Schauen Sie sich dieses Beispiel im Detail an: 1
Es wurde eine Klasse namens BaseClass in der CodeBehind-Datei definiert und die Subfunktion Calculate aus dem aspx-Formular in diese Klasse verschoben. Diese Klasse stammt aus der System.Web.UI.Page.
2
Die Textwerte der Textboxen Qty und Price wurden extrahiert, die beiden Werte multipliziert und das Ergebnis in die Amount-Textbox eingegeben. Da aus der Code Behind-Datei auf die Eigenschaftswerte der drei Textfelder (Textbox) zugegriffen werden muss, sind dort drei Textfelder mit der gleichen id deklariert: Protected qty as textbox Protected price as textbox Protected amount as textbox
3
Die Textboxen Qty, Price, Amount sind Web-Kontrollen, da sie mit dem asp:tag prefix initialisiert wurden. So wird zum Beispiel das Qty-Textfeld folgendermaßen erstellt: Da sich die Kontrollen in der Datei Sytem.Web. UI.WebControls befinden, muss das Namensfeld importiert werden, bevor über den Code auf die Eigenschaften zugegriffen werden kann. Dies geschieht durch den Import-Befehl oben auf der Seite: Imports System.Web.UI.WebControls
4
Zuletzt wurde das Page_Load-Event geschrieben, um bei seiner Initialisierung eine Mitteilung anzeigen zu können. Dieses Event wird bei jedem Laden der Seite gestartet. Manchmal ist es notwendig, Events zu programmieren, die nur beim Laden der Startseite angezeigt werden und nicht beim Aufruf der Folgeseiten. So kann zum Beispiel eine Web-Kontrolle an eine Datenquelle gebunden werden (weitere Informationen hierzu sind in Kapitel 4 Datenbindung enthalten), so dass dieses Event nur einmal beim Laden einer Seite eintritt.
Einführung in die ASP.NET-Webformulare 2 Mit der PostBack-Eigenschaft können Sie festlegen, ob das Senden einer Nachricht für eine Seite bereits stattgefunden hat. Verwenden Sie den folgenden Code, um beim ersten Laden einer Seite eine Nachricht anzuzeigen: IF NOT (isPostBack) Response.write("The not isPostBack construct:>_*) End if
Server-Kontrollen Es ist bereits oft versucht worden, HTML-Rendering in Kontrollen einzubinden. Hierzu dienten Objekte wie etwa VBX, OLEund ActiveX-Kontrollen, die einen einfachen Weg zur Generierung von HTML bieten sollten. Das Problem dieser Objekte war, dass der Benutzer immer den aktuellsten Browser benötigte, um diese Seiten öffnen zu können. Die in ASP.NET enthaltenen serverseitigen Kontrollen stellen solche Anforderungen nicht. Sie rendern reines HTML für jeden Browser und umgehen so die Unzulänglichkeiten älterer Objekte. Diese Server-Kontrollen sind vollintegrierte Objekte, die Events, Eigenschaften und Methoden für einen programmatischen Zugang ermöglichen. Sie existieren unabhängig von dem Webformular, für das sie generiert wurden. ASP.NET beinhaltet Web- und HTML-Kontrollen. Letztere entsprechen den gleichnamigen, traditionellen HTML-Kontrollen. Web-Kontrollen bieten Optionen wie etwa automatische Browser-Erkennung, ein konsistentes Objektmodell und die Fähigkeit zur Datenbindung.
HTML-Kontrollen HTML-Kontrollen gehören zu den Namensfeldern der Datei System.Web.UI.HTMLControls und sind von der Basisklasse HTMLControl abgeleitet. Sie werden durch das Attribut runat = "server" initialisiert. Die folgende HTML erstellt beispielsweise eine Instanz HTMLInputText namens textbox 1:
37
ASP.NET 38
2 Diese Kontrollen entsprechen ihren HTML-Gegenstücken und ermöglichen eine Abwärtskompatibilität mit ASP. Sie ermöglichen keine Abstraktion wie ihre Webcontrol-Gegenstücke und können Browser nicht automatisch erkennen, um den HTMLCode entsprechend zu modifizieren. Der Hauptnutzen dieser Kontrollen besteht darin, einen schnellen Zugangspfad zu ASP.NET zu ermöglichen, da die bestehenden HTML-Vorzeichen durch Hinzufügen des Attributs runat = "server" zu Server-Kontrollen aktualisiert werden können. Die folgenden Beispiele für HTML-Kontrollen werden im Anschluss genauer erläutert. Abbildung 2.2 zeigt einige dieser HTML-Kontrollen. Listing 2.6: HtmlControls.aspx HTML CONTROLS HTMLAnchor Controls >button id="Button1" style="font: 8pt verdana;background-color:light-green;bordercolor:black;height=30;width:100" runat="server"> HTMLButton HTMLImage HTMLInputButton: HTMLInputCheckBox: HTMLInputHidden :(hidden) Titles:
129
ASP.NET 130
4
Der Code Behind dieses Formulars ist MasterChild.vb. Listing 4.13: MasterChild.vb Imports Imports Imports Imports Imports Imports Imports
System System.Collections System.Text System.Data System.Data.OleDb System.Web.UI System.Web.UI.WebControls
Public Class BaseClass Inherits System.Web.UI.Page Protected AuthorsGrid as DataGrid Protected titlesGrid as DataGrid Protected detailsPanel as Panel Public currentAuthor as object Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim ConnStr As String Dim SQL As String Sub Page_Load(Source As Object, E As EventArgs) ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=pubs;User ID=sa;" myConnection = New OleDbConnection(ConnStr) if NOT (isPostBack) rebind end if End Sub Sub FillDs sql = " select * , au_lname + ',' + au_fname as au_name" sql = sql + " From authors a, titles t, titleauthor ta" sql = sql + " Where a.au_id = ta.au_id AND t.title_id = ta.title_id"
Datenbindung 4 myCommand = New OleDbDataAdapter(SQL, myConnection) 'Benutze die Fill-Methode des DataSetCommand, um einen Datensatz zu füllen myCommand.Fill(ds, "Authors") End Sub Sub ReBind() FillDs 'Bindung eines Datengitters AuthorsGrid.DataSource=ds.Tables("Authors").DefaultView AuthorsGrid.DataBind() End Sub Sub Grid_Select(sender as Object , e as EventArgs) Dim vIndex As Integer Dim vkey As string vIndex = AuthorsGrid.SelectedIndex vkey =AuthorsGrid.DataKeys(vIndex).ToString UpdateSelection(vkey) End Sub Sub UpdateSelection(vkey as string) Dim myConnection2 As OleDbConnection Dim myCommand2 As OleDbDataAdapter Dim ds2 As New DataSet Dim ConnStr2 As String Dim SQL2 As String Dim itemcount As Integer sql2 = " select * , au_lname + ',' + au_fname as au_name" sql2 = sql2 + " From authors a, titles t, titleauthor ta" sql2 = sql2 + " Where a.au_id = ta.au_id AND t.title_id = ta.title_id" sql2 = sql2 + " AND a.au_id = '" + vkey + "'" myCommand2 = New OleDbDataAdapter(SQL2, myConnection) myCommand2.Fill(ds2, "Authors") 'Bindung des Datengitters titlesGrid.DataSource=ds2.Tables("Authors").DefaultView titlesGrid.DataBind() itemcount = titlesGrid.Items.Count if itemcount >= 1 then detailsPanel.Visible = true Else
131
ASP.NET 132
4 detailsPanel.Visible = false response.write("No rows found") end if End Sub End Class
Die Master-Grid-Implementierung besteht aus zwei DataGrids: dem AuthorsGrid und dem titlesGrid. Das AuthorsGrid ist an eine Anfrage gebunden, die sich aus den Tabellen Authors, titles und TitleAuthors zusammensetzt. Diese Anfrage sieht folgendermaßen aus: sql = " select * , au_lname + ',' + au_fname as au_name" sql = sql + " From authors a, titles t, titleauthor ta" sql = sql + " Where a.au_id = ta.au_id AND t.title_id = ta.title_id"
Es wurde eine Button-Spalte mit CommandName="select" erstellt. Der Befehlsname select informiert das Datagrid, dass ein Objekt ausgewählt wurde, und startet das OnSelectedIndexChanged-Event, welches wiederum die Grid_Select-Funktion auslöst. Die Grid_Select-Funktion findet die au_id (das Datenschlüsselfeld) der Reihe, die sich verändert hat, und ruft die UpdateSelection-Funktion auf. Die UpdateSelection-Funktion bindet das titlesGrid mit der folgenden Anfrage: sql2 = " select au_name" sql2 = sql2 + " ta" sql2 = sql2 + " = ta.title_id" sql2 = sql2 + "
* , au_lname + ',' + au_fname as From authors a, titles t, titleauthor Where a.au_id = ta.au_id AND t.title_id AND a.au_id = '" + vkey + "'"
Diese Anfrage begrenzt die ursprüngliche Frage auf den ausgewählten Autor. Beachten Sie, dass das titlesGrid in einer Leiste mit der ID DetailsPanel eingeschlossen ist, die ursprünglich unsichtbar war. UpdateSelection bindet das titlesGrid und macht die Leiste sichtbar. Hierdurch werden die Detail-Datensätze zurückgegeben.
Datenbindung 4
Zusammenfassung In diesem Kapitel wurde eine ganze Reihe von Themen behandelt. Sie haben erfahren, wie ListBound-Kontrollen an eine Datenquelle gebunden werden. Das Kapitel enthielt praktische Beispiele zur Verwendung des DataRepeater, der DataList und des DataGrid. Auch die Arbeit mit XML-Datenquellen wurde erklärt. Die weiteren Kapitel bauen auf diesen Konzepten auf. In den Projekt-Abschnitten dieses Buches werden Sie lernen, diese Funktionen in aktuellen Anwendungen zu benutzen.
133
Eingabeüberprüfung
Ein zweigeteilter Zugang zur Validierung
137
Validierungs-Kontrollen
138
Die ValidationSummary-Kontrolle
148
Die IsValid-Eigenschaft
149
Deaktivierung der clientseitigen Validierung
149
Zusammenfassung
149
ASP.NET 136
5 Eingabeüberprüfung (Validation Control) ist eine langweilige und fade Aufgabe. Nur sehr wenige Entwickler haben Spaß an der Programmierung von Überprüfungscodes (Validation Codes). Dennoch sollte die Bedeutung einer guten Überprüfungstechnik nicht unterschätzt werden. Ohne eine brauchbare Eingabeüberprüfung würden unsere Skriptroutinen zusammenbrechen und in den Datenbanken würde sich nur unlesbarer Zeichensalat ansammeln. Mit ASP.NET wird die Implementierung von Validierungsroutinen ein Kinderspiel, da hierbei lediglich eine Überprüfungskontrolle erstellt werden muss, der per Definition eine zu überprüfende Kontrolle zugewiesen wird. Die Entwickler von ASP.NET haben sich die Mühe gemacht, zahlreiche Dateneingabe-Formulare zu untersuchen. Dabei fanden sie heraus, dass die meisten Überprüfungsaufgaben die folgenden Aktivitäten beinhalten: Suche nach regulären Ausdrücken, wie Zipcodes und Telefonnummern Vergleich von zwei Benutzereingaben Überprüfung der erforderlichen Felder (Required Field) Überprüfung, ob eine Eingabe in einen bestimmten Wertebereich fällt Des Weiteren stellte sich heraus, dass es möglich ist, den Benutzer sofort über einen falschen Eingabewert zu informieren. Zusätzlich können alle falschen Eingaben zusammengefasst und dem Benutzer gleichzeitig angezeigt werden. Mit diesem Wissen machten sich die Entwickler daran, ein Objekt zu erstellen, das all diese Aufgaben in sich vereint. Ein solches Objekt in einer ActiveX-Umgebung zu programmieren, würde eine Überladung der geforderten Funktionaliät in einer einzigen Komponente, die sich in verschiedenen Modi unterschiedlich verhalten müsste, zur Folge haben. Im .NET-Framework war es jedoch möglich, sechs Kontrollen zu erstellen, die alle aus einem gemeinsamen Objekt entstammen (BaseValidator). Jedes dieser Objekte erfüllt bestimmte Funktionen. Da der Aufgabenbereich jedes der Objekte begrenzt ist, entsteht hierdurch eine einfache, aber sehr effektive Kontrolle. Die folgende Liste enthält die zur Verfügung stehenden Kontrollen.
Eingabeüberprüfung 5 RequiredFieldValidator RegularExpressionValidator CompareValidator RangeValidator CustomValidator ValidationSummary Damit stehen fünf Validierungs-Kontrollen und eine Zusammenfassungs-Kontrolle (ValidationSummary) zur Verfügung. Die Aufgabe der ersten vier Kontrollen ergibt sich aus ihrem Namen. Sie können jedoch auch Ihre eigenen ÜberprüfungsFunktionen erstellen und diese mit dem CustomValidator assoziieren. Die ValidationSummary«-Kontrolle zeigt eine Zusammenfassung aller Eingabefehler auf einer Seite an.
Ein zweigeteilter Zugang zur Validierung Eine gute Validierung erfordert, dass die Eingaben des Benutzers auf der Server- und auf der Clientseite überprüft werden. Eine Überprüfung auf der Clientseite ist eine feine Sache. Der Benutzer wird unverzüglich über ungültige Eingaben informiert. Der Nachteil hierbei besteht darin, dass die Implementierung einer solchen Überprüfung in HTML 3.2 recht umständlich ist. Mit Skriptsprachen und DHTML jedoch ist diese Aufgabe leicht zu bewältigen. Damit die Überprüfung funktioniert, muss der Benutzer allerdings einen Browser verwenden, der diese Technologie unterstützt. Dies ist keine besonders günstige Voraussetzung. Außerdem bergen reine clientseitige Überprüfungen ein hohes Sicherheitsrisiko, denn hierdurch wird es sehr einfach, eine Skriptseite zu verfälschen, zu ersetzen oder zu umgehen. Aus diesem Grunde benutzen Überprüfungs-Kontrollen einen zweigeteilten Zugang. Die Kontrolle auf der Clientseite dient dazu, dem Benutzer ein sofortiges Feedback bieten zu können. Auf der Serverseite werden die Eingaben dann ein weiteres Mal überprüft. Im Internet Explorer 4.0 oder höher ist die Überprüfung auf der Clientseite automatisiert. Bei nicht skriptfähigen Browsern werden die Eingaben auf der Serverseite überprüft. Die Überprüfung auf der Clientseite bietet eine Anzahl von Optionen. So kann zum Beispiel eine Rückmeldung in roter Schrift
137
ASP.NET 138
5 an den Benutzer erfolgen, wenn dieser eine oder mehrere ungültige Eingaben gemacht hat. Diese Meldung verschwindet, wenn die Eingaben korrigiert wurden. Wenn sich ein Fehler auf der Clientseite nicht mehr beheben lässt, wird eine Rücksendung an den Server verhindert. Für diese Funktionalität werden keine ActiveX-Objekte oder Applets verwendet, da alle hierfür nötigen Codes in der JScript-Bibliothek vorhanden sind.
Validierungs-Kontrollen Im folgenden wird eine Website mit einem Beispiel für jede Validierungs-Kontrolle erstellt, die für die weiteren Ausführungen verwendet werden. Abbildung 5.1 zeigt das Ergebnis des validate.aspx-Codes.
Validierungs-Kontrollen Abb. 5.1
Listing 5.1: validate.aspx Sub ServerValidate (sender As Object, value As ServerValidateEventArgs) Dim num As Int32 = Int32.Parse(value.Value)
Eingabeüberprüfung 5 response.write(num) if num= "10" then value.IsValid = True else value.IsValid = False end if End Sub public sub OnSubmit(source as Object, e as EventArgs) if Page.IsValid then 'Überprüfe vor Update der Datenbank end if end sub Validating with ASP+ Controls ASP .NET Control Validation Required Field : Name : * Password: Confirm: * Range Validator IQ (180 – 265): Regular Expression: validation expression : [0-9]{3}\s[0-9]{3}-[09]{4} Example : 214 345-0458 Phone: Custom Validation: Must be the number 10    This field must be the number 10!
Required Field Die Required Field-Kontrolle (erforderliches Feld) gewährleistet, dass bestimmte Felder auch wirklich ausgefüllt werden. Hier ein Beispiel für eine Required Field-Kontrolle: Name : *
Der Benutzer muss hier die txtName-Textbox ausfüllen. Wenn er dies versäumt und den Submit-Button (Senden) anklickt, wird von der ValidationSummary-Kontrolle eine Fehlermeldung angezeigt, die ihn dazu auffordert, einen Namen einzugeben. Zusätzlich wird neben der entsprechenden Textbox ein Sternchen eingeblendet. Die ControlToValidate-Eigenschaft spezifiziert die ID der zu überprüfenden Kontrolle, in diesem Fall also txtName.
Compare Validator Der CompareValidator vergleicht den Inhalt zweier Kontrollen und zeigt eine Fehlermeldung an, wenn diese nicht übereinstimmen. Eine typische Anwendung des CompareValidator besteht im Vergleich der Eingabe eines Passwortes und dessen Wiederholung. Hier ein Beispiel: Password: Confirm: *
In diesem Beispiel werden zwei Felder verglichen. Wenn Sie nicht übereinstimmen, werden von der Validation-Kontrolle ein Sternchen und die Fehlermeldung Das Passwort stimmt nicht mit der Bestätigung überein (The password does not match the confirm password!) erzeugt, sobald der Submit-Button angeklickt wird. Sie können die Art der Werte und den vergleichenden Operator bestimmen. 1
Wenn Sie das Sternchen in der CompareValidatorMarkierung auslassen, wird statt des Sternchens eine Fehlermeldung angezeigt, um eine sofortige Rückmeldung an den Benutzer zu gewährleisten.
2
Folgende Eigenschaftstypen sind möglich: String Integer Double DateTime Currency
3
Folgende Operatoren sind möglich: Equal NotEqual GreaterThan GreaterThanEqual LessThan
Folgende Display-Eigenschaften sind möglich: None: Bestimmt, dass keine Fehlermeldung angezeigt werden soll. Die Kontrolle wird trotzdem überprüft und gefundene Fehler werden von der Validation-Kontrolle angezeigt. Dynamic: Legt fest, dass die Kontrolle Platz für die Fehlermeldung beansprucht, wodurch sich das Layout der Seite verändert. Dies ist besonders sinnvoll, wenn mehrere ÜberprüfungsKontrollen an eine Kontrolle gehängt wurden. Static: Legt fest, dass für die gesamte Fehlermeldung Platz auf der Seite reserviert wird. In diesem Fall ändert sich das Layout der Seite nicht, wenn eine Fehlermeldung angezeigt wird.
5
Der CompareValidator ist gültig, wenn ein Feld nicht ausgefüllt wurde.
RangeValidator Sie können den RangeValidator benutzen, um zu überprüfen, ob die Eingaben eines Benutzers in einen bestimmten Wertebereich fallen. Im folgenden Beispiel prüft der RangeValidator, ob der Eingabe-IQ zwischen 180 und 265 liegt. Range Validator IQ (180 – 265):
143
ASP.NET 144
5 Der RangeValidator verfügt über die Eigenschaften ControlToValidate, Display und Type, die bereits beschreiben wurden. Hinzu kommen die Eigenschaften MinimumValue und MaximumValue. Diese spezifizieren die obere und untere Grenze des Wertebereichs, in dem die Eingabe des Benutzers liegen muss. Der RangeValidator ist auch gültig, wenn ein Feld nicht ausgefüllt wurde.
Regular Expression Der RegularExpressionValidator ist die leistungsfähigste aller Kontrollen. Er prüft die Eingabe des Benutzers im Vergleich zu einem vom Entwickler vorgegebenen Zeichenmuster. Reguläre Ausdrücke (Regular Expressions) haben ihre eigene Syntax. Um zum Beispiel festzulegen, dass eine Telefonnummer in dem Format xxx xxx xxxx eingegeben wird, können Sie den regulären Ausdruck [0-9] (3)\s[0-9](3)-[0-9](4) eingeben. In diesem Fall müssen die ersten drei eingegebenen Ziffern zwischen 0 und 9 liegen und ihnen muss ein Leerschritt folgen. Danach folgen drei Ziffern, die wieder zwischen 0 und 9 liegen müssen und denen ein Querstrich folgen muss. Die letzten drei eingegebenen Ziffern müssen ebenfalls zwischen 0 und 9 liegen. Diese Kontrolle kann verwendet werden, um gültige E-Mail-Adressen, Zipcodes und jedes andere benutzerdefinierte Muster zu prüfen. Das folgende Beispiel zeigt einen RegularExpressionValidator, der eine Telefonnummer nach dem Muster xxx-xxx-xxxx prüft. Phone:
Wie Sie sehen, verfügt der RegularExpressionValidator über die Eigenschaften ControlToValidate, Display und ErrorMessages. Diese Eigenschaften haben die gleichen Begriffsinhalte wie die anderen Überprüfungs-Kontrollen. In der Validation-Eigenschaft werden die Muster der regulären Ausdrücke festgelegt.
Eingabeüberprüfung 5 Der RegularExpressionValidator ist gültig, wenn ein Feld nicht ausgefüllt wurde.
Musterfibel der regulären Ausdrücke Ein regulärer Ausdruck ist eine aus Zeichen bestehende Sequenz, die ein Muster (oder ein Objekt) vorgibt, mit dem die Eingabe eines Benutzers übereinstimmen muss. Übereinstimmung mit dem Muster
Das Muster Hersh stimmt mit der Zeichenkombination Hersh überein, sofern diese Zeichen zusammen und in dieser Reihenfolge eingegeben werden. her.h würde mit Herjh, Hersh und Herdh übereinstimmen, da der Dezimalpunkt für jedes Zeichen außer für den Gedankenstrich einsetzbar ist. Nehmen Sie nun an, Sie möchten dem Wort Hersh eine Zahl zwischen 0 und 9 voranstellen. In diesem Fall würden Sie [09]Hersh eingeben. Die eckigen Klammern umschließen einen vorgegebenen Zeichensatz. Zur Übereinstimmung reicht es, wenn der Benutzer eines dieser Zeichen eingibt. Das Muster [a-z] umschließt alle kleingeschriebenen Buchstaben des Alphabets, während das Muster [a-zA-Z] auch die großgeschriebenen Buchstaben umfasst. Für die Suche nach Zahlen in einem String wird das Muster [0-9] verwendet. Wiederholte Übereinstimmung
Stellen Sie sich vor, Sie wollten alle Übereinstimmungen zu den Wörtern Book und Books finden. Hierfür müssten Sie das Muster Book? benutzen. Das Fragezeichen stimmt mit dem vorhergehenden Zeichen 0 oder 1 mal überein (z.B. wird nur nach einem weiteren Zeichen nach Book gesucht, welches dann an das Wort angefügt wird). Wenn Sie nun nach Wörtern suchen, die mit Book beginnen und dieses Wort um mehr als nur ein Zeichen erweitern, müssen Sie anstelle des Fragezeichens ein Sternchen anhängen. Das Muster würde nun Book* heißen. Für dieses Muster werden Wörter wie Booked, Bookie, Bookworm etc. zurückgegeben. Der Ausdruck (n) stimmt mit dem Zielstring genau n-mal überein. p (2) zum Beipiel stimmt mit happy, aber nicht mit hop überein.
145
ASP.NET 146
5 Übereinstimmung der Positionen
Wenn Sie beispielsweise alle Wörter, die mit Gr beginnen, finden wollen, müssen Sie das Zirkumflexsymbol verwenden. Ihr Muster sieht dann so aus: ^Gr. Dieses Muster stimmt mit Wörtern wie Great, Grate, Grr etc. überein. Das Zirkumflexsymbol stimmt mit dem Anfang der Zeile überein. Das Dollarsymbol ($) stimmt mit dem Ende der Zeile überein. Wenn Sie daher Wörter finden möchten, die mit d enden, müssten Sie das Muster d$ verwenden. Hierfür werden Wörter wie had, bad, lad etc. zurückgegeben.
Benutzerdefinierte Überprüfung Der CustomValidator kann zur Erweiterung der Überprüfungsfunktionen verwendet werden. Er ermöglicht die Programmierung benutzerdefinierter Funktionen und Routinen. Dies ist besonders nützlich, wenn ein Teil der Überprüfungsroutinen den Zugriff auf Informationen aus einer Datenbank erfordert. Sie können eine Server-Funktion und optional auch eine ClientFunktion definieren, die vom CustomValidator aufgerufen werden. Die Funktionen auf der Server- und auf der Clientseite sind gleich. Routinen auf der Clientseite, die leicht vom Benutzer umgangen werden können, sind nicht mehr nötig. Clientroutinen sollten nur noch verwendet werden, um den Benutzer bei einer ungültigen Eingabe unverzüglich über den Fehler zu informieren. Die tatächliche Überprüfungsfunktionalität sollte auf der Serverseite liegen. Hier ein Beispiel: Custom Validation: Must be the number 10    This field must be the number 10!
Eingabeüberprüfung 5 In diesem Beispiel muss der Benutzer die Zahl 10 eingeben. Jede andere Eingabe führt zu einer Fehlermeldung durch die benutzerdefinierte Client-Funktion, die zurückgegeben wird, sobald der Benutzer den Submit-Button (Senden) drückt.
Serverfunktionalität Die Funktionen der Serverseite sind durch die OnServerValidateEigenschaft definiert. Diese wurde so eingestellt, dass sie die Funktion ServerValidate auslöst, die folgendermaßen aussieht: Sub ServerValidate (sender As Object, value As ServerValidateEventArgs) Dim num As Int32 = Int32.Parse(value.Value) response.write(num) if num= "10" then value.IsValid = True else value.IsValid = False end if End Sub
Hierdurch wird geprüft, ob die Zahl 10 eingegeben wurde. Wenn nicht, wird der Zustand false zurückgegeben.
Funktionalität auf der Clientseite Die Funktionalität auf der Clientseite wird durch die Eigenschaft ClientValidateFunction definiert. Diese löst die benutzerdefinierte Funktion ClientValidate aus. Diese Funktion kann entweder in JScript oder VBScript geschrieben worden sein. Das Gerüst dieser Funktion ist im folgenden Beispiel enthalten:
Der Code dieser Funktion wird immer dann ausgelöst, wenn der Benutzer etwas in das Feld eingibt und es dann mit der Tabulatortaste verlässt. Der Code gibt die Zustände true oder
147
ASP.NET 148
5 false zurück, wodurch die Fehlermeldungs-Funktion ein- oder ausgeschaltet wird. Wenn bei älteren Browsern die Clientüberprüfung ausgeschaltet wurde, wird diese Funktion nicht aufgerufen. Die CustomValidation-Kontrolle überprüft den Browser automatisch und rendert das entsprechende HTML. Sie sollten das Skript der Clientseite jedoch immer in HTML integrieren, so dass es von älteren Browsern ignoriert wird. Zwei Parameter werden an die Client-Funktion weitergegeben. (Diese entsprechen den an die Server-Funktion weitergegebenen Parametern.) Sie sind das Element der Clientüberprüfung und der durch ControlToValidate definierte Wert der Kontrolle. In der Client-Funktion können Sie wählen, ob Sie diese Parameterdefinitionen im Aufruf Ihrer Funktion ignorieren wollen. Der CustomValidator ist gültig, wenn das Feld leer ist.
Die ValidationSummary-Kontrolle ValidationSummary ist die letzte Kontrolle. Diese Kontrolle sammelt die Werte aus der Fehlermeldungs-Eigenschaft aller Überprüfungs-Kontrollen der Seite und zeigt diese zusammen an. Die Validation-Kontrolle kann überall auf der Seite platziert werden. Sie können auch festlegen, wie die Fehler angezeigt werden sollen (Aufzählungspunkte, einfache Liste etc.). Hier ein Beispiel: asp:ValidationSummary runat=server displaymode ="bulletlist" showsummary = "true" headertext="There were errors on the page:" />
Die Validation-Kontrolle hat folgende Eigenschaften: HeaderText: Überschrift der Fehlerliste. DisplayMode: Mögliche Anzeigearten sind List (getrennt durch ), BulletList (in der Grundeinstellung durch
getrennt) und SingleParagraph (alle Fehler in einem Abschnitt). ShowSummary: True (Grundeinstellung) oder False. Bei False wird nur die Überschrift angezeigt. ShowMessageBox: True oder False (Grundeinstellung). Bei True wird statt der Zusammenfassung eine PopupMeldung angezeigt.
Eingabeüberprüfung 5
Die IsValid-Eigenschaft Hierbei handelt es sich um eine Seiteneigenschaft, die true zurückgibt, wenn keine Fehler gefunden wurden und false, wenn Fehler aufgetreten sind. Diese Eigenschaft sollte vor der Aktualisierung einer Datenbank unbedingt geprüft werden. Hier ein Beispiel für eine Überprüfung gesendeter Formulare: Public sub OnSubmit(source as object, e as eventargs) If Page.IsValid then //Datenbank-Update durchführen End if End Sub
Deaktivierung der clientseitigen Validierung In manchen Fällen kann es erforderlich sein, die Überprüfung auf der Clientseite zu deaktivieren. Möglicherweise haben Sie einen Code, der nur auf der Serverseite ausgeführt wird, so dass eine Überprüfung auf der Clientseite nicht notwendig ist. Zur Deaktivierung der Überprüfung benutzen Sie die Direktive clienttarget=downlevel. Der Code sieht folgendermaßen aus:
Der voreingestellte Wert dieser Direktive ist auto. Dies bedeutet, dass eine Überprüfung auf der Clientseite nur für Internet Explorer 4.0 oder höher erfolgt.
Zusammenfassung Die Validation-Kontrollen nehmen Ihnen die Arbeit des Schreibens von Überprüfungscodes ab. Sie sind intelligent und können HTML entsprechend dem angewandten Browser rendern. Diese Kontrollen sind eine willkommene Hilfe, von der Sie in den nächsten Kapiteln ausgiebig Gebrauch machen werden.
149
Benutzer-Kontrollen
Erstellung einer einfachen BenutzerKontrolle
152
Darstellung von Eigenschaften
154
Entwurf eines Navigations-Systems für Ihre Website mit einer BenutzerKontrolle
156
Zusammenfassung
161
ASP.NET 152
6 Code Behind-Techniken trennen den visuellen Teil und das Skript in separate Formulare. Das Webformular übernimmt die Codeklassen in die Seite. Eine Benutzer-Kontrolle jedoch kann eine eigenständige Entity sein, die gleichermaßen aus dem graphischen und dem Skriptcode besteht. Eine Benutzer-Kontrolle bietet die Funktionalität (und Einfachheit) von echten ASP-Elementen der Serverseite. Die enthaltenen Dateien sind jedoch statisch, wohingegen Benutzer-Kontrollen Objektmodelle unterstützen, so dass hier gegen die Eigenschaften und Benutzermethoden programmiert werden kann. Die Funktion ähnelt der von inhärenten ASP.NET-Kontrollen, das heißt, diese Kontrollen sind auch in der Lage, Eigenschaften und Methoden zu exponieren. ASP.NET-Webformulare müssen in einer einzigen Sprache geschrieben werden. Benutzer-Kontrollen umgehen diese Beschränkungen, da diese auf der gleichen Website in verschiedenen Sprachen geschrieben werden können. Sie können ebenfalls mehrmals auf einer Seite angewendet werden, ohne dass hierdurch Konflikte entstehen, da jede Kontrolle ihr eigenes Namensfeld hat. Eine Benutzer-Kontrolle ist eine einfache Textdatei, die ohne die Erweiterung ascx« gespeichert wird. Sie enthält keine -, - und -Markierungen. Die Seite, welche das Benutzer-Objekt aufruft, wendet diese Vorzeichen an. Mit einigen kleinen Veränderungen kann jede Website in eine Benutzer-Kontrolle konvertiert werden.
Erstellung einer einfachen BenutzerKontrolle Hier wird erklärt, wie Sie eine Benutzer-Kontrolle erstellen, die eine einzige Textzeile mit dem Inhalt This is a Test Control anzeigt, wenn sie angestoßen wird. 1
Erstellen Sie eine Datei und speichern Sie diese unter dem Namen simpleUC1.ascx.
2
Fügen Sie eine einzelne Textzeile mit dem Inhalt This is a Test Control ein.
3
Erstellen Sie ein Webformular simpleUC1.aspx und fügen Sie die folgenden Markierungen hinzu:
Benutzer-Kontrollen 6
153
Listing 6.1: simpleUC1.aspx 4
Öffnen Sie das Webformular mit Ihrem Browser.
Die Register-Direktive registriert die Kontrolle zusammen mit dem Webformular. Der Benutzer-Kontrolle wurde das TagPrefix namens Hersh gegeben. Hier kann jeder beliebige Name eingesetzt werden. Der TagName ist der Alias-Name der Benutzer-Kontrolle, der als Bezug benutzt wird. Das einfache Beispiel zeigt, dass sich die Benutzer-Kontrolle ähnlich verhält wie die im Webformular enthaltenen Dateien.
Eine einfache BenutzerKontrolle Abb. 6.1
ASP.NET 154
6
Darstellung von Eigenschaften Visual Basic.NET und C# bieten einen praktischen Weg der Zuordnung und Auffindung von Werten durch die Accessor- und Mutator-Funktionen. Anstelle der Let-, Get- und Set-Statements aus VB 6.0 (die jetzt nicht mehr unterstützt werden) benötigen Sie lediglich einen einzigen Eigenschaftsblock (Property block), der die Set- und Get-Blöcke enthält. Das folgende Beispiel enthält einen message-Eigenschaftsblock: Public Property message As String Get Return Txt.Value End Get Set Txt.Value = Value End Set End Property
Die Accessor-Funktionen (Get-Funktionen) werden aufgerufen, wenn die message-Eigenschaft Werte anfordert. Gleichermaßen wird die Mutator-Funktion (Set-Funktion) aufgerufen, wenn der message-Eigenschaft Werte zugeordnet werden. Eine Benutzer-Kontrolle kann Eigenschaften freilegen, die dann vom Benutzer konfiguriert werden können. Der Inhalt einer Benutzer-Kontrolle kann hierdurch so verkapselt werden, dass der Benutzer der inneren Funktion einer Komponente verborgen bleibt. Im folgenden Beispiel besteht die BenutzerKontrolle aus einer einzigen Textbox, der ein String Message passed from a Web form gegeben wurde. Abbildung 6.2 zeigt die entsprechenden Auswirkungen auf das Webformular. Listing 6.2: simpleUC2.aspx
Benutzer-Kontrollen 6
155
Das Formular der Benutzer-Kontrolle enthält den folgenden Code: Listing 6.3: simpleUC2.ascx Public Property message As String Get Return Txt.Value End Get Set Txt.Value = Value End Set End Property
Anzeigen von Eigenschaften in Benutzer-Kontrollen Abb. 6.2
Benutzer-Kontrollen haben ihre eigenen Namensfelder. Aus diesem Grund geraten die Variablennamen einer BenutzerKontrolle nicht mit einer anderen Benutzer-Kontrolle, inhärenten Kontrolle oder sogar mit Seitenvariablen in Konflikt.
ASP.NET 156
6 Es ist empfehlenswert, Eigenschaften anstelle von Public-Variablen sichtbar zu machen. Eigenschaften ermöglichen es Ihnen, Daten zu verstecken, sie können variiert werden und werden von visuellen Design-Programmen wie Visual Studio.NET unterstützt. Im Beispiel wurde eine Eigenschaft namens message definiert, die den weitergegebenen MessageText der Textbox zuordnet.
Entwurf eines Navigations-Systems für Ihre Website mit einer BenutzerKontrolle Jedes Mal, wenn Sie Ihrer Website eine weitere Seite hinzufügen, müssen Sie alle Web-Formulare aktualisieren, um Ihre Navigationsstruktur anzupassen. Wenn ich eine Seite aufbaue, speichere ich alle Links in einer XML-Datei. Dann verschmelze ich die XML-Navigationsdatei mit der XML-Hauptdatei und rendere sie durch Anwendung von Style Sheets. Hierbei benutze ich eine Kombination aus MSXML- und XSL-Style Sheets. Die Implementierung der gleichen Funktionaliät in ASP.NET ist dabei ein Kinderspiel. Ich verwalte die Links meiner Seite in einer XML-Datei mit der folgenden Struktur: Listing 6.4: nav.XML Home default.aspx Masters masters.aspx
Diese Benutzer-Kontrolle kann mit einem Aufruf wie dem folgenden in jedem Web-Formular platziert werden.
Die Benutzer-Kontrolle befindet sich in der Datei nav.ascx. Eine Website, die diese Kontrolle aufruft, registriert dieselbe mit der folgenden Direktive:
Die Kontrolle wird dann durch die folgenden Markierungen dargestellt: form runat=server>
Beachten Sie, dass die Komponente sich innerhalb der Formularmarkierungen der aufrufenden Seite befindet und dass hierdurch drei Eigenschaften festgelegt werden: vGridLines, vBorderControl und vCellPadding.
157
ASP.NET 158
6 Die folgende Liste erklärt die Benutzer-Kontrolle: 1
Weitergabe der Website-Parameter an die Komponente:
Die Benutzer-Kontrolle definiert drei Public-Variablen, die drei weitergegebene Variablen erhalten: PUBLIC vGridLines As GridLines PUBLIC vBorderColor as String PUBLIC vCellPadding As Integer 2
Einlesen der XML-Datei in das DataSet: Im page_loadEvent wird die Datei gelesen und im DataSet gespeichert. Ein DataGrid wird an das DataSet gebunden, wobei die Attribute GridLines, BorderColor und CellPadding mit den weitergegebenen Variablen festgelegt werden. Sub Page_Load(Source As Object, E As EventArgs) Dim ds As New DataSet Dim fs As filestream Dim xmLStream As StreamReader fs = New filestream(Server.MapPath("nav.xml"), FileMode.Open, FileAccess.Read) xmlStream = new StreamReader(fs) ds.ReadXML(XmlStream) fs.Close() dlist.DataSource=ds.Tables("site").DefaultView dlist.DataBind() dlist.GridLines = vGridLines dlist.BorderColor=System.Drawing.Color.FromName(v BorderColor) dlist.CellPadding=vCellPadding End Sub
3
Bestimmung der BorderColor-Eigenschaft: Die BorderColor-Eigenschaft ist datenbezogen. Um einen String zu einer Farbe zu konvertieren, wird die FromName-Methode verwendet. Dadurch wird vBorderColor wie im folgenden Beispiel als String deklariert: PUBLIC vBorderColor as string
Benutzer-Kontrollen 6 Mit Hilfe des folgenden Codes habe ich die BorderColor-Eigenschaft definiert: dlist.BorderColor=System.Drawing.Color.FromName(v BorderColor) 4
Die DataList: Die DataList stellt die Navigationslinks
dar. Es wurde eine Hyperlink-Kontrolle definiert, um die Links zu rendern. Diese Kontrolle ist an die SiteURLMarkierung der XML-Datei gebunden.
Sie sehen, wie einfach die Implementierung ist. Dadurch, dass sich die Links in einer separaten XML-Datei befinden, ist es möglich, die Navigations-Struktur einer Website zu modifizieren, ohne hierfür alle Web-Formulare ändern zu müssen.
159
ASP.NET 160
6 Es folgt eine komplette Auflistung der Benutzer-Kontrolle: Listing 6.6: nav.ascx 'Öffentliche Variable der dargestellten Eigenschaft PUBLIC vGridLines As GridLines PUBLIC vBorderColor As String PUBLIC vCellPadding As Integer Sub Page_Load(Source As Object, E As EventArgs) Dim ds As New DataSet Dim fs As filestream Dim xmLStream As StreamReader fs = New filestream(Server.MapPath("nav.xml"), FileMode.Open, FileAccess.Read) xmlStream = new StreamReader(fs) ds.ReadXML(XmlStream) fs.Close() dlist.DataSource=ds.Tables("site").DefaultView dlist.DataBind() dlist.GridLines = vGridLines dlist.BorderColor=System.Drawing.Color.FromName(vBorderC olor) dlist.CellPadding=vCellPadding End Sub
Zusammenfassung Benutzer-Kontrollen können Präsentations- und Codelogik beinhalten. In diesem Kapitel haben Sie eine sehr nützliche Komponente erstellt, welche die Navigations-Funktion der Website enthält. Sie haben gelernt, dass Benutzer-Kontrollen einfach zu erstellen und mit kleinen Modifikationen für jedes WebFormular verwendbar sind.
161
Benutzerdefinierte Kontrollen
Eine einfache Kontrolle in Visual Basic
165
Erstellen einer einfachen Kontrolle in C#
168
Die generische, benutzerdefinierte Edit/Add-Kontrolle
171
Die Config-Datei
175
Erstellen der Kontrolle
179
Benutzung der benutzerdefinierten GenEditAdd-Kontrolle
207
Zusammenfassung
211
ASP.NET 164
7 Die Entwicklung von Kontrollen ist oft mit der Entwicklung von Komponenten für ein Produkt verglichen worden. Die Zusammensetzung eines Produkts an einem Montageband umfasst die Montage verschiedener Komponenten. Auf ähnliche Weise werden Softwarekontrollen entwickelt, die generische Funktionalität enthalten. Diese Kontrollen werden dann mit anderen Komponenten zum Softwareendprodukt zusammengefügt. Die ASP.NET-Server-Kontrollen DataList, DataGrid und DataRepeater sind Beispiele solcher Softwarekontrollen. Kontrollen gibt es schon seit einer ganzen Weile. Hierbei haben sich ActiveX-Kontrollen und Java-Applets in den letzten Jahren immer stärker profiliert. Eine Einschränkung besteht darin, dass nicht alle Websites diese Technologien unterstützen. Mit dem Aufkommen einer neuen Produktgeneration mit Handheld-Geräten und Mobiltelefonen wurde alles noch komplizierter. Wir können nicht voraussetzen, dass unsere Zielgruppen die zur Ausführung und Unterstützung unserer Kontrollen notwendige Technologie besitzen. Die ASP.NETServer-Kontrollen versuchen diesem Problem entgegenzuwirken, indem sie den Server reines HTML rendern lassen, das von allen Browsern verstanden wird. ASP.NET bietet einen sauberen Weg zur Entwicklung von benutzerdefinierten Kontrollen. Im Folgenden wird zunächst eine einfache Kontrolle erstellt, die Ihnen einen Überblick über den ganzen Prozess gibt. Als Nächstes wird eine sehr nützliche benutzerdefinierte Kontrolle entwickelt, eine generische Add/Modify-Kontrolle (Hinzufügen/Ändern). Ein DataGrid ist nicht in der Lage, Datensätze hinzuzufügen. In den bisher für dieses Buch erstellten Webformularen wurden Textboxen und Codes verwendet, um diese Funktionalität zu integrieren. Die benutzerdefinierte Kontrolle, die für dieses Kapitel erstellt wird, akzeptiert einen SQL-Abfragestring als Eigenschaftseinstellung und generiert automatisch ein Formular, um Datensätze komplett mit Feldnamen zur Datenbank hinzuzufügen. Wenn Sie hier den Namen einer gespeicherten Prozedur eingeben, wird die Kontrolle diese mit den erforderlichen Parametern aufrufen, um einen Datensatz zu erstellen. Diese Kontrolle verfügt auch über einen Editiermodus. Sie können die Kontrolle an ein DataGrid hängen und dieses zum Editieren verwenden. Der Vorteil hierbei ist, dass Sie keine Events für das DataGrid programmieren müssen. Zu diesem Thema ent-
Benutzerdefinierte Kontrollen 7 hält das Kapitel eine einfache Kontrolle, die zunächst in Visual Basic und dann in C# geschrieben wurde.
Eine einfache Kontrolle in Visual Basic Diese Kontrolle zeigt einfach nur eine Meldung mit dem Inhalt Hello World an. Diese Kontrolle ist einfach zu erstellen und lässt sich dennoch in eine funktionierende DLL kompilieren und demonstriert die verschiedenen Entwicklungsschritte. Der Quellcode für dieses Beispiel befindet sich auf der CD dieses Buches.
Schritt 1: Erstellen einer klassischen Datei Benutzen Sie Notepad, um eine Visual Basic Datei zu erstellen, und speichern Sie diese mit der Erweiterung .vb. Listing 7.1: hello.vb Imports System Imports System.Web Imports System.Web.UI Namespace Hersh Public Class Hello: Inherits Control Protected Overrides Sub Render(Output As HtmlTextWriter) Output.Write("Hello World") End Sub End Class End Namespace
Hier eine Erklärung der verschiedenen Aspekte des Codes: 1
Namespaces: Namespaces ermöglichen die Gruppierung von verbundenen Klassen, Benutzeroberflächen, Strukturen, Aufzählungen und Delegationen unter einem Namen. Namespaces können festgesetzt werden und die Quelldatei kann beliebig viele Namespaces haben. In diesem Fall befindet sich die Klasse Hello im Namespace Hersh. In dem aufrufenden Webformular wird der Namespace mit der folgenden Direktive registriert:
165
ASP.NET 166
7
Die Kontrolle wird folgendermaßen aufgerufen: 2
Imports: Die import-Direktive ist eine weitere Abkürzung, die es ermöglicht, Namespaces zu benutzen, ohne einen kompletten Pfad einzugeben. Die von einer Server-Kontrolle benötigten Namespaces sind System, System.Web, System.Web.UI und System.Web.UI.WebControls. Der System-Namespace enthält die Hauptsystemklassen wie Class-Activation und Class-Serialization. System.Web enthält Web-bezogene Dienste, wie etwa HttpRequest und HttpResponse. System.Web.UI enthält Services wie das Kontrollmanagement und kaskadenartiges StyleSheet-Management. System.Web.UI.Control enthält die Definition der Kontroll-Klasse zur Erstellung der Kontrolle. Die Kontroll-Klasse enthält Eigenschaften, Methoden und Events, die allen Server-Kontrollen in einer ASP.NET-Page eigen sind. ASP.NET unterstützt Inhärenz, so dass eine neue Kontrolle aus einer Kontroll-Klasse mit Hilfe des InheritsSchlüsselworts erstellt werden kann. Funktionen der Originalklasse können dabei umgangen werden, um die spezifische Funktionalität der daraus hervorgehenden Klasse gewährleisten zu können. Public: Beachten Sie, dass die Hello-Klasse als Public deklariert wurde. Eine Public-Klasse kann von allen genutzt werden, während Private-Klassen nur von Klassen mit dem gleichen Namespace genutzt werden können. Die Render-Funktion: Die Hello-Klasse ist aus der Control-Klasse abgeleitet, die auch die Render-Funktion enthält. Die Render-Funktion
Benutzerdefinierte Kontrollen 7 bestimmt das Aussehen der Kontrolle. Um das Ergebnis einer Kontrolle selbst bestimmen zu können, muss diese Funktion umgangen und die Write-Methode des HtmlTextWriter benutzt werden, um den String Hello World vom Browser darstellen zu lassen. Render benötigt ein einziges Argument des Typs HtmlTextWriter, welches die Arbeit der Übertragung von HTML an den Browser abstrahiert. Diese Klasse ist in der Lage, Elementmarkierungen, Attribute und Stile automatisch zu generieren. Weitere Funktionen dieser Klasse sind unter anderem WriteLineNoTabs, WriteBeginTag und WriteEndTag.
Schritt 2: Erstellen der DLL Erstellen Sie einen bin-Ordner in wwwroot (oder der Root-Richtung Ihres Anwendungsordners) und führen Sie die Datei makevb.bat aus. (Vergewissern Sie sich, dass die outdir-Variable auf Ihren bin-Ordner gerichtet ist.) Diese Datei enthält die folgenden Befehle: Listing 7.2: makeVb.bat set outdir=g:\AspNetSamples\bin\hello.DLL set assemblies=System.dll,System.Web.dll vbc /t:library /out:%outdir% /r:%assemblies% hello.vb pause
Hierdurch wird die Datei hello.dll im bin-Ordner erstellt. In ASP.NET wurde die Registrierung der Komponenten weitgehend verbessert. Zuvor musste hierzu ein Registrierungs-Tool verwendet werden (regsvr32.exe). Jetzt müssen Sie lediglich die DLL in den bin-Ordner kopieren. Um die Komponente zu aktualisieren genügt es, die aktuelle DLL in den bin-Ordner zu kopieren. Es erfolgen keine Eintragungen in die Registry, so dass Sie die Komponente lediglich aus dem bin-Ordner löschen müssen, um sie komplett zu löschen. bin ist ein spezielles Verzeichnis, welches das .NET-RuntimeModul prüft, um Namensfelder (Namespaces) zu identifizieren und zu lokalisieren. NET-Runtime prüft die Metadaten der Dateien im bin-Verzeichnis, von denen es weiß, an welcher Stelle
167
ASP.NET 168
7 im Webformular sich die entsprechenden Namensfelder (Namespaces) befinden. Metadaten eines Objekts beinhalten jene Informationen, die benötigt werden, um das Objekt zu benutzen. Im Allgemeinen enthalten diese Informationen den Namen des Objekts, die Namen aller Felder des Objekts, deren Typ sowie die Details aller einbezogenen Funktionen einschließlich der Parametertypen und Namen. Unter Assembly (Zusammenbau) versteht man die Zusammenfügung aller für ein Objekt benötigten Dateien in einem Paket. Der NET-Compiler kompiliert den Code in eine Zwischenform namens IL (Intermediate Language). Dieses Paket enthält ILMetadaten und andere Dateien in einer umfassenden Sprache. Es ist möglich, für jede Anwendung einen bin-Ordner zu erstellen. Die hierin enthaltenen Komponenten sind für diese Anwendung lokal.
Schritt 3: Erstellen des Webformulars Listing 7.3: hellovb.aspx
Es wurde der Register-Befehl zur Registrierung der im Namensfeld (Namespace) enthaltenen Funktionalität verwendet. Nun ist es einfach, die Klasse mit Hilfe der -Markierung (tag) zu initiieren.
Erstellen einer einfachen Kontrolle in C# In diesem Abschnitt wird die Erstellung einer Kontrolle in C# erklärt. Diese Kontrolle rendert ebenfalls im Browser den Satz Hello World. In diesem Beispiel wird jedoch eine Eigenschaft namens message definiert, die zur Anzeige des Strings Hello World verwendet wird. Diese Kontrolle zeigt auch das Datum
Benutzerdefinierte Kontrollen 7 und die Uhrzeit im System an. Der Quellcode für dieses Beispiel befindet sich im Hello World-Unterverzeichnis auf der CD des Buchs.
Schritt 1: Erstellen einer klassischen Datei Listing 7.4: helloC.cs using System; using System.Web.UI; using System.ComponentModel; namespace CustomControls { public class FirstC : Control { private String message = ""; public virtual String Message { get { return message; } set { message = value; } } protected override void Render( HtmlTextWriter writer { writer.Write(" "+ this.Message + "" + "The server date and time : " + System.DateTime.Now + ""); } } }
Dieser Code ist beinahe identisch mit dem Visual Basic-Code. Es gibt allerdings einige kleine Unterschiede in der Syntax, die in der folgenden Liste aufgeführt sind:
169
ASP.NET 170
7 1
Using anstelle von Imports Visual Basic: Imports System C# : using System;
2
NameSpace Visual Basic: NameSpace Hersh 'Some Code here End NameSpace
C#: NameSpace{ //Some code here } 3
Inherits
Visual Basic: Public Class Hello: Inherits Control C#: public class FirstC : Control Eine Eigenschaft namens message wird so definiert, dass sie die Zugriffsmethoden get und set enthält. Der String Hello World wird mit dieser Eigenschaft weitergegeben.
Schritt 2: Erstellen der DLL Die folgenden Befehle kompilieren die Kontrolle in eine DLL: Listing 7.5: makec.bat set outdir=G:\AspNetSamples\bin\helloc.DLL set assemblies=System.Web.dll csc /t:library /out:%outdir% /r:%assemblies% helloC.cs pause
Hierdurch wird eine Kontrolle namens helloc.dll im bin-Verzeichnis erstellt.
Benutzerdefinierte Kontrollen 7 Schritt 3: Erstellen des Webformulars Erstellen Sie nun folgendermaßen das Webformular, das diese Kontrolle aufruft:
Die Kontrolle befindet sich im Namensfeld (Namespace) CustomControls. Der folgende Code initiiert die Kontrolle und übermittelt ihr den String Hello World in C#:
Die generische, benutzerdefinierte Edit/Add-Kontrolle Diese Kontrolle hat eine Geschichte. In ASP war die Anzeige von Daten aus einer Datenbank in einem Browser keine leicht zu bewältigende Aufgabe. Man musste einen Code schreiben, um eine Datenbankverbindung zu erstellen, den Datensatz mit Daten aus der Datenbank füllen und diesen dann wiederholen, um die Daten als HTML zu rendern. Viele kluge Köpfe der ASPSzene machten sich daran, generische Routinen zu entwickeln, um das Rendern von Datenbanken zu automatisieren. Alan Saldanha schrieb ein Handbuch, in dem er die Entwicklung solch eines generischen Tools beschrieb. Eli Robillard entwickelte ein großartiges Tool aufgrund dieser Theorie und stellte es auf seiner Website (http://www.ofifc.org/Eli/ASP/GenericArticle.asp) als Freeware zur Verfügung. Eli nannte sein Tool, das mittlerweile für eine ganze Reihe von Websites verwendet wurde, Genericdb. Um eine Datenbanktabelle (oder das Ergebnis einer SQL-Abfrage) zu erstellen, musste man lediglich eine config-
171
ASP.NET 172
7 Datei erstellen, in der verschiedene Variablen wie etwa der Tabellenname, die DSN, die Anzeigefelder etc. definiert wurden. Die Tabelle ähnelte dem DataGrid in ASP.NET und hatte ähnliche Optionen, wie wechselnde Farben, Seitenwechsel und Sortierung (Paging und Sorting). Eli nannte diese Seite Lister. Das Gitter verfügte über drei Links: Hinzufügen, Löschen und Modifizieren (Add, Delete, Modify). Mit einem Klick auf diese Links war es möglich, einen Datensatz hinzuzufügen, zu aktualisieren oder zu löschen. Roman Koch entwickelte EDITOR.ASP (http://www.4guysfromrolla.com/webtech/110999-1.shtml), nachdem er Genericdb gesehen hatte. Es war eine einfache Version von Genericdb, geschrieben als eine Serie von Funktionsaufrufen. Koch schaffte es, einen einfachen Lister und einen Editor in eine einzige 10KByte große Datei zu packen. Das ASP.NET-DataGrid beinhaltet die Lister-Funktionalität des Genericdb. Mit anderen Worten: Es zeigt eine Liste von Datensätzen an, die durch Klicken auf den Editierlink modifiziert werden können. Der Editiermodus des DataGrid entspricht der Editor-Funktionalität des Genericdb. Dennoch ist diese Funktionalität nicht automatisiert. Hier müssen eine Reihe von DataGrid-Events programmiert (z.B. OnEditCommand, OnUpdateCommand, OnCancelCommand etc.), die modifizierten Werte eingelesen und die aktualisierten Werte zurück an die Datenbank geschickt werden. Ein weiterer Nachteil des Editiermodus liegt darin, dass das Layout des Gitters in diesem Modus horizontal ist. Besser wäre hier das in Webformularen weiter verbreitete vertikale Layout. Das DataGrid bietet keinen Eingabemodus (Insert). Wenn Sie diese Funktionalität in Ihre Website integrieren wollen, müssen Sie auf die traditionelle Methode zurückgreifen und durch Hinzufügen von Textboxen ein Eingabeformular für den Benutzer erstellen. Diese Erwägungen brachten mich dazu, die benutzerdefinierte GenEditAdd-Kontrolle zu entwickeln. Diese Kontrolle, die auch eine konsistente Editier- und Eingabe-Funktionalität (Edit und Insert) beinhaltet, kann an ein DataGrid gehängt werden. Sie müssen keine Events programmieren; es reicht, wenn Sie einige Eigenschaften festlegen.
Benutzerdefinierte Kontrollen 7
173
Ich empfehle Ihnen, die GenEditAdd-Kontrolle zunächst einmal zu kompilieren und damit zu experimentieren. Das Muster für diese Kontrolle finden Sie im Unterverzeichnis GenEditAdd_ final auf der CD des Buches. Die Kontrolle wird im weiteren Verlauf noch genauer beschrieben. Zum Testen benötigen Sie die Masters-Tabelle in Ihrer Datenbank. Wenn Sie die Beispieldatenbank noch nicht installiert haben, sollten Sie das jetzt tun. Die Anleitung hierzu finden Sie im Anhang A. Hier eine Anleitung für den Test: 1
Starten Sie die bat-Datei mGenEditAdd.bat. Hierdurch wird die Kontrolle (GenEditAdd.DLL) in den bin-Ordner unter www.root kopiert. Achten Sie darauf, die Datei so zu modifizieren, dass sie auf Ihren bin-Ordner ausgerichtet ist.
2
Öffnen Sie masters.aspx und führen Sie es durch ILS aus.
Das masters.aspx-Formular enthält ein DataGrid. Betrachten Sie das Aussehen einen Augenblick. Ich werde den Code später erklären. Dieses Webformular zeigt alle Datensätze der Masters-Tabelle an. Jeder Reihe sind drei Links zugeordnet: Hinzufügen, Editieren und Löschen. Abbildung 7.1 zeigt das Formular.
Die GenEditAdd-Kontrolle im Add-Modus Abb. 7.1
ASP.NET 174
7 Durch Klicken auf den Editier-Link (Edit) wird die GenEditAddKontrolle im Editiermodus aktiviert. Ich kann Modifizierungen anbringen und den Update-Link anklicken. Hierdurch werden die Änderungen in der Datenbank gespeichert. Abbildung 7.2 zeigt die GenEditAdd-Kontrolle im Editiermodus. Ein Klick auf den Add-Link aktiviert die Kontrolle im Add-Modus (Hinzufügen). Abbildung 7.3 zeigt die GenEditAdd-Kontrolle im Add-Modus.
Der Lister Abb. 7.2
Die Kontrolle erstellt automatisch einen Prozeduraufruf (Procedure Call) der gespeicherten Prozedur p_masters. Im Moment reicht es zu wissen, dass diese Prozedur für die Einfügung und Aktualisierung eines Masters-Datensatzes verantwortlich ist. Um einen Datensatz zu verändern, senden Sie ihm den primären Schlüssel (Primary Key) des Datensatzes, um diesen als Parameter zu modifizieren. Der Codewert (code_value) ist der primäre Schlüssel der Masters-Tabelle. Um daher einen Datensatz mit dem Codewert 3 zu aktualisieren, rufen Sie die gespeicherte Prozedur folgendermaßen auf: Execute p_masters @code_display='Visa Card ', @code_category=604, @type='A', @closing=700, @code_value=3
Benutzerdefinierte Kontrollen 7
175
Die GenEditAdd-Kontrolle im Editiermodus Abb. 7.3
Wenn Sie einen neuen Datensatz eingeben möchten, senden Sie einen Null-Wert als Codewert (code_value) an diese Prozedur, wie im folgenden Beispiel: Execute p_masters @code_display='Test', @code_category=1, @type='', @closing=10, @code_value=NULL
Dieser String muss nicht manuell erstellt werden, da die GenEditAdd-Kontrolle die Benutzereingabe liest, um die Eingabewerte zu extrahieren, so wie sie die Feldsammlung liest, um die Spaltennamen zu erhalten. Der String zum Aufruf dieser Prozedur wird automatisch erstellt und an die Datenbank weitergegeben.
Die Config-Datei Das DataGrid im Masters-Webformular verfügt über zwei Hyperlinkspalten: Eine für den Edit-Link (Bearbeiten) und eine zweite für den Add-Link (Hinzufügen). Diese Links führen zum config_masters.aspx-Formular. Der Edit-Link gibt den primären Schlüssel des Datensatzes weiter (in diesem Fall code_value: der Codewert):
ASP.NET 176
7
Der Add-Link gibt den Codewert 0 weiter:
Das config_masters.aspx-Formular beherbergt die GenEditAddKontrolle. Jedes DataGrid, das die Edit- und Add-Funktionalität anfordert, definiert ein separates Konfigurationsformular (config form). Dieses Formular enthält die GenEditAdd-Kontrolle und einige Eigenschaftseinstellungen für diese Komponente. Der an das Formular weitergegebene Codewert (code_value) wird im Page Load-Event extrahiert. Auch hier werden einige Eigenschaften spezifiziert. Sehen Sie sich das folgende Beispiel an: Listing 7.6: The Config_Masters.aspx form Sub page_load(sender As Object, e As EventArgs) if NOT (isPostBack) Dim sql As string Dim ls_CodeValue As string ls_CodeValue = Request.QueryString("code_value") SQL = "Select * from masters" Gen.sql = SQL if cint(ls_codeValue) = 0 then Gen.Where = "" else Gen.where= " Where code_value =" + ls_CodeValue end if Gen.display = "111110" Gen.KeyField = "code_value" Gen.KeyValue = ls_codeValue Gen.procedure = "p_masters"
Benutzerdefinierte Kontrollen 7 Gen.ExitPage = "masters.aspx" end if End Sub
In diesem Formular wird die GenEditAdd-Kontrolle zuerst mit der folgenden Seitendirektive (page directive) registriert:
Die Kontrolle wird dann mit der ID Gen erstellt:
Die GenEditAdd-Kontrolle benötigt einige Eigenschaftseinstellungen. Dies geschieht im Page Load-Event. Die folgende Tabelle listet die Eigenschaften der GenEditAdd-Komponente auf: 1
Eigenschaft: SQL Bedeutung: Der SQL-String ohne die Where-Bedingung Beispiel: Select * from Masters
2
Eigenschaft: Where Bedeutung: Wenn die Where-Eigenschaft gegeben ist, zeigt GenEditAdd den Editiermodus an. Bleibt die Eigenschaft leer, wird der Eingabe-Modus (Insert) angezeigt. Die Where-Bedingung wird dynamisch im Config-Formular auf der Basis des weitergegebenen Codewerts erstellt:
177
ASP.NET 178
7 ls_CodeValue = Request.QueryString("code_value") SQL = "Select * from masters" Gen.sql = SQL If cint(ls_codeValue) = 0 Then Gen.Where = "" Else Gen.where= " Where code_value =" + ls_CodeValue End If
Bei einem an den Abfragestring weitergegebenen Codewert von 0 wird die Eigenschaft Where offen gelassen und die Kontrolle im Eingabemodus (Insert) angezeigt. Ist die Where-Eigenschaft gegeben, wird eine Where-Bedingung erstellt und die Kontrolle im Editiermodus (Edit) angezeigt. 3
Eigenschaft: Display (Anzeige) Bedeutung: Dies ist ein String aus 0 und 1. 0 bedeutet, dass kein Feld angezeigt werden soll. Ist der Wert 1 eingetragen, wird en Feld angezeigt. Nehmen Sie an, die Masters-Tabelle hätte 4 Felder. Der String 0101 würde bedeuten, dass die Felder 1 und 3 nicht angezeigt werden. Beispiel: 0101
4
Eigenschaft: KeyField Bedeutung: Der primäre Schlüssel-Feldname Beispiel: code_value
5
Eigenschaft: KeyValue Bedeutung: Der Wert des primären Schlüssels Beispiel: 2 (Dieser Wert wird vom aufrufenden Formular an das config-Formular weitergegeben.)
6
Eigenschaft: Procedure Bedeutung: Die gespeicherte Prozedur, die aufgerufen wird, um einen Datensatz einzufügen oder zu aktualisieren. Beispiel: p_masters
Benutzerdefinierte Kontrollen 7 7
Eigenschaft: ExitPage Bedeutung: Erstellt einen Hyperlink oben auf dem Edit/Add-Formular, mit dem Sie zurück zum aufrufenden Formular gelangen. Beispiel: masters_box
8
Eigenschaft: ConnStr Bedeutung: String)
Der
Verbindungsstring
(Connection
Beispiel: Provider=SQLOLEDB; DataSource=(local); Initial_ Catalog=ASPNET; UserID=sa; Acht Eigenschaften müssen für die GenEditAdd-Komponente festgelegt werden. Der Code der Kontrolle befindet sich in der Datei GenEditAdd.vb. Ich werde diese Kontrolle nun erstellen und dabei den Vorgang der Erstellung von benutzerdefinierten Kontrollen erklären.
Erstellen der Kontrolle Der komplette Quellcode dieser benutzerdefinierten Kontrolle befindet sich in der Datei GenEditAdd_chap7.vb. Der Code in dieser Datei ist ziemlich umfangreich und bedarf einiger Erläuterungen. Das gesamte Thema wird daher schrittweise erklärt. Ich werde eine Datei mit einem Zwischencode erstellen, diesen dann kompilieren und bis zur Erstellung der endgültigen Kontrolle in der Datei GenEditAdd_chap7.vb einige ihrer Aspekte erklären. Der Code dieser Arbeitsschritte befindet sich im Unterverzeichnis steps auf der CD des Buches.
Schritt 1: Der Editiermodus Mit diesem Schritt beginnt die Erstellung der Kontrolle. Step1.aspx ist ein einfaches aspx-Formular, das zeigt, wie ein Benutzereingabeformular im Editiermodus der Kontrolle erstellt wird. Für die Anzeige des GenEditAdd im Editiermodus werden ein SQL-String sowie die Bedingung Where benötigt. Dieser feste Code des Webformulars kann in der GenEditAddKontrolle zu einer Eigenschaft konvertiert werden. Abbildung 7.4 zeigt das hieraus entstehende Webformular.
179
ASP.NET 180
7
Die GenEditAddKomponente im Editiermodus Abb. 7.4
Listing 7.7: Step1.aspx Sub Page_Load(sender As Object, e As EventArgs) Dim dv As DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim ConnStr As String Dim SQL As String Dim Where As String ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;" myConnection = New OleDbConnection(ConnStr) SQL = "select * from groups " Where = "where code_value = 700" myCommand = New OleDbDataAdapter(SQL + Where, myConnection) myCommand.Fill(ds, "groups") dv = new DataView(ds.Tables("groups")) Dim t As DataTable
Benutzerdefinierte Kontrollen 7 t = dv.Table Dim r As DataRow Dim c As DataColumn Dim cell As TableCell Dim row As DataRow For Each r in t.Rows For Each c in t.Columns response.write(c.ToString + ": ") response.write(r(c).ToString + "") Next c Next r End Sub
Das SQL-Stement und die Where-Bedingung sehen folgendermaßen aus: SQL = "select * from groups " Where = "where code_value = 700"
Es wird ein DataSet mit den von dieser Abfrage zurückgegebenen Daten geladen und einer Datenansicht (Data View) zugeordnet. Diese Datenansicht wird einer Datentabelle (DataTable) zugewiesen, wodurch die Sammlung der Datenspalten (DataColumn/die innere Schleife) für jede Datenreihe (DataRow/die äußere Schleife) wiederholt werden kann: dv = new DataView(ds.Tables("groups")) Dim t As DataTable t = dv.Table Dim r As DataRow Dim c As DataColumn Dim cell as TableCell Dim row as DataRow For Each r in t.Rows For Each c in t.Columns
181
ASP.NET 182
7 response.write(c.ToString() + ": ") response.write(r(c).ToString() + "") Next c Next r
Ich erhalte den Spaltennamen (c.ToString) und zeige ihn als Label in der GenEditAdd-Kontrolle, die in Schritt 3 erstellt wird, in einer vom Benutzer editierbaren Textbox an.
Schritt 2: Der Add-Modus Ich habe noch immer nicht mit der Erstellung der Kontrolle begonnen. Step2.aspx ist ein Webformular, welches den Code im Add-Modus (Hinzufügen) erklärt. In diesem Modus wird keine Where-Bedingung benötigt. Sie brauchen jedoch immer noch die SQL-Bedingung, da die Spaltensammlung gelesen werden muss, um die Spaltennamen zu extrahieren und diese als Label gegenüber den Feldern anzuzeigen. Hier der Code aus Step2.aspx: Listing 7.8: Step2.aspx Sub Page_Load(sender As Object, e As EventArgs) Dim dv As DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim ConnStr As String Dim SQL As String ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;" myConnection = New OleDbConnection(ConnStr) SQL = "select * from groups " myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "groups") dv = new DataView(ds.Tables("groups")) Dim t As DataTable t = dv.Table
Benutzerdefinierte Kontrollen 7 Dim r As DataRow Dim c As DataColumn Dim cell as TableCell Dim row as DataRow For Each c in t.Columns response.write(c.ToString() + " ") Next c End Sub
Der Add-Modus Abb. 7.5
Im Eingabemodus (Insert) muss lediglich die DataColumnsSammlung wiederholt werden. Abbildung 7.5 zeigt das Ergebnis dieses Formulars.
183
ASP.NET 184
7 Schritt 3: Der erste Aufbau Jetzt kann mit dem Aufbau der Kontrolle begonnen werden. Zuvor sollten Sie jedoch ein Webformular erstellen, das es Ihnen ermöglicht, die Kontrolle während des Aufbaus zu testen. Der Code für diesen Schritt befindet sich auf der CD des Buches. Listing 7.9: GenTestStep3.aspx Sub page_load(sender As Object, e As EventArgs) if NOT (isPostBack) Dim vsql As string Gen.sql = "select * from masters" Gen.where= " Where code_value = 1" Gen.display = "111110" Gen.KeyField = "code_value" Gen.KeyValue = "1" Gen.procedure = "p_masters" Gen.ExitPage = "GenTestStep3.aspx" end if End Sub
Dieses Webformular initiiert die GenEditAdd-Kontrolle und legt deren acht Eigenschaften fest. Die Benutzer-Kontrolle erscheint im Editiermodus, wenn die Where-Bedingung an sie weitergegeben wird. Um die Kontrolle im Add-Modus anzuzeigen, muss für die Where-Bedingung ein leerer Wert eingegeben werden.
Benutzerdefinierte Kontrollen 7 In diesem Arbeitsschritt wird der erste Teil der Kontrolle erstellt. Der Code befindet sich in der Datei Step3.vb und sollte durch Ausführen von Step3.bat kompiliert werden. Editieren Sie die bat-Datei so, dass die outdir-Variable auf Ihren bin-Ordner gerichtet ist. Die folgende Auflistung der Codedatei wird im Anschluss näher erläutert: Listing 7.10: Step3.vb Option Strict Off Imports System Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Data Imports System.Data.OleDb Namespace Generic_Chap7_step3 Public Class GenEditAdd_Chap7 : Inherits Control : Implements INamingContainer Private ls_display as string Private ls_where as string Private ls_sql as string Private ls_ConnStr as string Private ls_keyField as string Private ls_keyValue as string Private ls_procedure as string Private ls_exitpage as string Private lt_datatable as datatable Private ls_mode as string Protected mytbl as table Public Property Mode as string Get Return Cstr(ViewState("ls_mode")) End Get Set ViewState("ls_mode") = value End Set End Property Public Property ExitPage as string Get Return Cstr(ViewState("ls_exitpage")) End Get Set ViewState("ls_exitpage") = value
185
ASP.NET 186
7 End Set End Property Public Property t as datatable Get Return lt_datatable End Get Set lt_datatable = value End Set End Property Public Property KeyField as string Get Return Cstr(ViewState("ls_keyfield")) End Get Set ViewState("ls_keyfield") = value End Set End Property Public Property KeyValue as string Get Return Cstr(ViewState("ls_keyvalue")) End Get Set ViewState("ls_keyvalue") = value End Set End Property Public Property Procedure as string Get Return Cstr(ViewState("ls_procedure")) End Get Set ViewState("ls_procedure") = value End Set End Property Public Property display as string Get Return Cstr(ViewState("ls_display")) End Get Set ViewState("ls_display") = value End Set End Property Public Property Where as string Get
Benutzerdefinierte Kontrollen 7 Return Cstr(ViewState("ls_where")) End Get Set ViewState("ls_where") = value End Set End Property Public Property SQL as string Get Return Cstr(ViewState("ls_sql")) End Get Set ViewState("ls_sql") = value End Set End Property Public Property ConnStr as string Get Return Cstr(ViewState("ls_ConnStr") ) End Get Set ViewState("ls_ConnStr") = value End Set End Property Protected Overrides Sub CreateChildControls() Dim dv As DataView Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim vSql As string If Where.Length < 1 then vSql = SQL mode = "insert" Else vSql = SQL + Where mode = "update" End If myConnection = New OleDbConnection(ConnStr) myCommand = New OleDbDataAdapter(vSql, myConnection) myCommand.Fill(ds, "vtable") dv = new DataView(ds.Tables("vtable")) Dim Fields As Integer ' t als DataTable t = dv.Table Dim r As DataRow
187
ASP.NET 188
7 Dim c As DataColumn Dim cell As TableCell Dim row As DataRow Dim Fieldscount As integer Dim s As string Dim vdisplay As string vDisplay = Display + "000000000000000000000000000000000000000000" FieldsCount = 0 s = "Back" me.Controls.Add(new LiteralControl(s)) me.Controls.Add(new LiteralControl("
")) me.Controls.Add(new LiteralControl("
")) If mode = "insert" then me.Controls.Add(new LiteralControl("
Add a New Record:
")) Else me.Controls.Add(new LiteralControl("
Edit Record:
")) End if me.Controls.Add(new LiteralControl("
")) If mode = "update" then For Each r in t.Rows For Each c in t.Columns 'Don't show this field IF vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField then Else me.Controls.Add(new LiteralControl("
")) end if FieldsCount = FieldsCount + 1 Next c Next r Else ' Insert Mode For Each c in t.Columns IF vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField then Else me.Controls.Add(new LiteralControl("
")) Dim Box As New TextBox Box.ID = c.ToString me.Controls.Add(box) me.Controls.Add(new LiteralControl("
")) End if FieldsCount = FieldsCount + 1 Next c End If me.Controls.Add(new LiteralControl("
")) me.Controls.Add(new LiteralControl("
")) '--------Button hinzufügen Dim AddButton As New Button if mode = "insert" then AddButton.Text = "Add" else AddButton.Text = "Update" end if Me.Controls.Add(AddButton) End Sub End Class End Namespace
189
ASP.NET 190
7 Eigenschaften und Namensfelder In diese Komponente wurden die folgenden Namensfelder (Namespaces) importiert: System System.Web System.Web.UI System.Web.UI.WebControls System.data System.data.ADO Durch die Definition der Eigenschaften der Kontrolle hat der Benutzer die Möglichkeit, gewünschte Werte in die Kontrolle einzugeben. Eigenschaften werden durch die Definition der Accessor(get)- und Mutator(set)-Funktionen und einer lokalen Variablen implementiert. Die GenEditAdd-Kontrolle benutzt die oben genannten acht Eigenschaftswerte und zwei Eigenschaften für den internen Gebrauch. Das ergibt eine Summe von zehn Eigenschaften. Es wurden daher die folgenden zehn Variablen deklariert: Private Private Private Private Private Private Private Private Private Private
ls_display as string ls_where as string ls_sql as string ls_ConnStr as string ls_keyField as string ls_keyValue as string ls_procedure as string ls_exitpage as string lt_datatable as datatable ls_mode as string
Hier eine Liste der zehn Eigenschaften: Display Where SQL ConnStr KeyField KeyValue
Benutzerdefinierte Kontrollen 7 Procedure ExitPage DataTable Mode (insert/edit) Die Eigenschaften DataTable (Datentabelle) und Mode (Modus) werden von der Kontrolle intern verwendet und können daher nicht von Benutzer festgelegt werden. Beachten Sie auch, dass für diese beiden die ViewState-Eigenschaft verwendet wurde. Die SQL-Eigenschaft sieht daher folgendermaßen aus: Public Property SQL as string Get Return Cstr(ViewState("ls_sql")) End Get Set ViewState("ls_sql") = value End Set End Property
Die vom Benutzer festgelegten Eigenschaften werden in einer so genannten StateBag gespeichert. Das ASP.NET-Framework speichert den Status der Kontrolle, wenn diese zerstört wird, und stellt die Werte bei deren Neuerstellung wieder her. Die ViewState-Eigenschaft bezieht die gespeicherten Eigenschaften aus der State-Bag. Ohne die State-Eigenschaft verlieren Sie jedesmal alle Eigenschaftswerte, wenn ein Formular zurückgesendet wird.
Komposition der Kontrolle Die einfache, am Anfang dieses Kapitel erstellte Kontrolle ist eine nicht kompositive Kontrolle, da das Rendern des HTML für den Browser gesteuert werden musste. Dies geschah durch Umgehung der Render-Methode. GenEditAdd ist eine kompositive Kontrolle, da sie aus standardmäßigen ASP.NET-Kontrollen wie Textboxen und Buttons zusammengesetzt wird. Die GenEditAdd-Kontrolle stammt aus der Control-Klasse. Diese Klasse beinhaltet eine umgehbare Kontrolle namens CreateChildControls. Zweck dieser Methode ist das Erstellen von untergeordneten Kontrollen im Formular. Dem Formular werden zwei Arten von Kontrollen, LiteralControls und TextBoxes,
191
ASP.NET 192
7 hinzugefügt. Jedes Mal, wenn Sie ein Label hinzufügen oder einen String schreiben wollen, der eine HTML-Formatierung enthält, benutzen Sie die LiteralControl. Um zum Beispiel Tabellenmarkierungen einzubringen, fügen Sie die folgende LiteralControl mit der Methode Controls.Add hinzu: me.Controls.Add(new LiteralControl("
"))
Es werden auch eine Reihe von Textboxen hinzugefügt, um das Eingabeformular (Input-Form) zu erstellen. Hierzu jedoch später mehr. Es wird der Modus identifiziert, in dem sich die Kontrolle befindet. Je nachdem, ob es sich hierbei um den Eingabe- oder Update-Modus handelt, wird die Mode-Eigenschaft entsprechend aktualisiert. Hierzu dient der folgende Code: If Where.Length < 1 then vSql = SQL mode = "insert" Else vSql = SQL + Where mode = "update" End If
Wenn die Where-Eigenschaft aus weniger als einem Zeichen besteht, wird die Mode-Eigenschaft auf Insert (Eingabe) gesetzt. Andernfalls wird die Mode-Eigenschaft aktualisiert und die Where-Bedingung wird dem SQL-Statement hinzugefügt. Ist die Mode-Eigenschaft auf Update gesetzt, wiederhole Sie die DataRows- und DataColumns-Sammlungen wie in Schritt 1 beschrieben. Andernfalls wiederhole Sie lediglich DataColumns entsprechend der Beschreibung in Schritt 2. Eine Reihe von Textboxen für Eingaben des Benutzers werden dem Formular auf folgende Weise hinzugefügt: Dim Box As New TextBox Box.Text = r(c).ToString Box.ID = c.ToString me.Controls.Add(box)
Nun, da jede Textbox ihre eigene ID besitzt, kann ich mich im Code darauf beziehen. Die ID ist der Name der Datenbankspalte. Die Textbox wird von den Markierungszeichen der Tabellendaten (
) eingeschlossen. Die Markierungen der
Benutzerdefinierte Kontrollen 7 Tabellen (
), Tabellenreihen (
) und der Tabellendaten (
) werden mit Hilfe der Literal-Kontrollen erstellt. Die Display-Eigenschaft (Anzeige) erlaubt es Ihnen zu spezifizieren, welche Felder angezeigt werden sollen. Stellen Sie sich vor, Ihre Datenbankfelder seien code_value, code_display, type, opening und closing. Sie möchten nicht, dass der Benutzer in der Lage ist, den primären Schlüssel zu ändern. Außerdem möchten Sie erreichen, dass die Felder type (wird von der gespeicherten Prozedur p_masters automatisch eingestellt und sollte vom Benutzer nicht geändert werden können) und closing Balance (wird von einem Datenbank-Trigger automatisch aktualisiert; dieser Trigger befindet sich in der Transaktionstabelle und wird später im Buch erklärt) nicht angezeigt werden. Sie geben für die Display-Eigenschaft daher den Wert 01010 ein. Sie erinnern sich sicher noch daran, dass 0 bedeutet, dass ein Feld nicht angezeigt wird, wohingegen die 1 die Einblendung des Feldes auslöst. Im Hauptteil der Kontrolle polstern Sie die Display-Eigenschaft mit einer Reihe von Nullen aus: vDisplay = Display + "000000000000000000000000000000000000000000"
Dieser Code dient als Ersatz für den Fall, dass der Benutzer die Eingabe dieses Wertes vergisst. Eine Display-Eigenschaft mit dem Wert 0 bringt die Kontrolle zum Absturz. Die Eigenschaft -property wird dann folgendermaßen bewertet: If vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField Then 'Do Nothing Else 'Proceed End if
Die Chars-Methode extrahiert den Wert eines Strings an einer vorgegebenen Stelle. Diese Position wird von der FieldsCountVariablen gesetzt, die in die Schleife eingebunden wird. Wenn das extrahierte Zeichen an dieser von der FieldsCount-Variablen gesetzten Position gleich 0 ist oder die Position sich in der Spalte KeyColumn befindet, macht die Kontrolle überhaupt nichts. Andernfalls wird eine Textbox mit der entsprechenden ID erstellt. Zum Schluss werden noch zwei Buttons hinzugefügt: ein Update-Button im Editiermodus und ein Insert-Button im Eingabemodus (Insert-Mode).
193
ASP.NET 194
7 Der InamingContainer Starten Sie das Test-Webformular GenTestStep3.aspx. Sehen Sie sich die Quelle im Browser an. Die folgende Liste ist ein Auszug daraus: A href=>Back
Edit Record:
code_display
code_category
type
closing
Beachten Sie, dass jede Textbox ein ID-Attribut hat. Der code_display hat beispielsweise die ID Gen_code_display. Die ID wurde im Hauptteil des Codes spezifiziert. Der Name Gen wurde jedoch von der Kontrolle selbst erstellt. Dies bedeutet, dass alle Textboxen (jede mit einer eigenen ID) sich innerhalb einer übergeordneten Kontrolle namens Gen befinden und dieser Name darum ihrer ID beigefügt wird. Ich habe dem ASP.NET-Runtime-Modul mitgeteilt, dass GenEditAdd ein InamingContainer ist, indem ich am Anfang des Codes das Statement Implements InamingContainer eingefügt habe. Hierdurch nimmt die Kontrolle am ID- und Statusmanagement teil. Um zu sehen, was diese Direktive bewirkt, löschen Sie Implements InamingContainer, so dass der Code nun folgendermaßen aussieht: Public Class GenEditAdd: Inherits Control
Kompilieren Sie Step3.vb (durch Ausführen von step3.bat). Starten Sie nun GenTestStep3.aspx und schauen Sie sich die Quelle des generierten HTML an: Back
Edit Record:
code_display
195
ASP.NET 196
7 code_category
type
closing
Beachten Sie, dass die Texbox-ID jetzt nicht mehr das Vorzeichen Gen enthält. Wenn Sie nun Änderungen in den Eingabefeldern vornehmen und den Update-Button anklicken, gehen alle Änderungen verloren, wenn das Formular zurückgesendet wird. (Der Status wird beipielsweise nicht beibehalten.)
Schritt 4: Die benutzerdefinierte GenEditAddKontrolle In diesem Schritt wird die GenEditAdd-Kontrolle fertig gestellt. Der endgültige Code ist in der Datei GenEditAdd.vb enthalten. Die Quelldatei für diesen Schritt finden Sie auf der CD des Buches. Kompilieren Sie die GenEditAdd-Kontrolle durch Ausführen der bat-Datei mGenEditAdd.bat.
Benutzerdefinierte Kontrollen 7 Umgang mit Events Die GenEditAdd-Kontrolle beinhaltet einen Button mit der Bezeichnung AddButton (Hinzufügen). Wenn dieser Button angeklickt wird, muss ein Aufruf an die gespeicherte Prozedur erfolgen, in dem die geänderten/neuen Werte weitergegeben werden, damit die Prozedur diese aktualisieren oder einen neuen Datensatz eingeben kann. Hierzu programmiere ich einen Event Handler. EventHandler sind durch Anbindung an die von den Subkontrollen ausgelösten Events an den Button gebunden. Für den AddButton sieht das folgendermaßen aus: AddHandler AddButton.Click, AddressOf AddBtn_Click
Hierdurch wird das ASP.NET-Runtime-Modul angewiesen, die Methode AddBtn_click zu starten, wenn der AddButton angeklickt wurde. Diese Methode enthält den zur Generierung des Prozeduraufrufs notwendigen Code. Hier die Codeliste der Methode: Private Sub AddBtn_Click(Sender As Object, E As EventArgs) 'Erstelle Prozeduraufruf Dim s As String Dim r As DataRow Dim c As DataColumn Dim cell As TableCell Dim row As DataRow Dim column As string Dim Value As string Dim Fieldscount As integer Dim vdisplay As string vDisplay = Display + "000000000000000000000000000000000000000" FieldsCount = 0 s = "Execute " + procedure + "" If mode = "update" Then For Each r in t.Rows For Each c in t.Columns If vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField Then Else Dim tb As TextBox tb = me.FindControl(c.ToString) column = c.ToString
197
ASP.NET 198
7 Value = tb.text If c.DataType.ToString = "System.String" Then s = s + " @" + column + "='" + value + "', " Else s = s + " @" + column + "=" + value + ", " End If End If FieldsCount = FieldsCount + 1 Next c Next r s = s + "@" + KeyField + "=" + KeyValue me.Controls.Add(new LiteralControl(s)) RunSql(s) Else For Each c in t.Columns If vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField Then Else Dim tb As TextBox tb = me.FindControl(c.ToString) column = c.ToString Value = tb.text If c.DataType.ToString = "System.String" Then s = s + " @" + column + "='" + value + "', " Else s = s + " @" + column + "=" + value + ", " End If End If FieldsCount = FieldsCount + 1 Next c s = s + "@" + KeyField + "=NULL" me.Controls.Add(new LiteralControl(s)) RunSql(s) End If End Sub
Der Code dieses Eventhandlers (Event-Verarbeitungsfunktion) folgt der gleichen Logik bezüglich der Eingabe- und Aktualisierungs-Modi (Insert und Update) wie in den Schritten 1 und 2 beschrieben. Der String eines Prozeduraufrufs ist auf dem Modus aufgebaut und von diesem abhängig. Der Codewert-Parameter (code-value) ist 0 (Eingabemodus) oder entspricht dem primären Schlüssel (primary key) des Datensatzes, der aktualisiert werden soll (Update-Modus). Der erstellte String er-
Benutzerdefinierte Kontrollen 7 scheint auf dem Bildschirm als LiteralControl, so dass Sie erkennen können, was an die Datenbank übermittelt wird. Zuletzt wird der String an das altbekannte RunSql weitergegeben, welches die eigentliche Arbeit der Interaktion mit der Datenbank ausführt. Schauen Sie sich die RunSql-Methode an: Sub RunSql(vSql as string) try Dim s As string Dim myConnection As OleDbConnection myConnection = New OleDbConnection(ConnStr) Dim mycommand As New OleDbCommand(vsql,myConnection) myconnection.Open() myCommand.ExecuteNonQuery() myconnection.Close() Catch ex As OleDbException 'SQL-Fehler Dim errItem As OleDbError Dim errString As String Dim s As string For Each errItem In ex.Errors errString += ex.Message + "" Next s = "SQL Error.Details follow:" & errString me.Controls.Add(new LiteralControl(s)) Catch myException as Exception me.Controls.Add(new LiteralControl("Exception: " + myException.ToString())) End try End Sub
Ich habe zwei aspx-Formulare eingefügt, die Sie zum Testen der Kontrolle im Insert- und Update-Modus benutzen können. Diese Formulare heißen GenTestStep4_insert.sapx und GenTestStep4_update.aspx. Damit ist die Erläuterung der benutzerdefinierten GenEditAddKontrolle beendet. Hier ist der komplette Code: Listing 7.11: GenEditAdd.vb Option Strict Off Imports System Imports System.Web
199
ASP.NET 200
7 Imports System.Web.UI Imports System.Web.UI.WebControls Imports System.Data Imports System.Data.OleDb Namespace Generic_Chap7 Public Class GenEditAdd_Chap7 : Inherits Control : Implements INamingContainer Private ls_display as string Private ls_where as string Private ls_sql as string Private ls_ConnStr as string Private ls_keyField as string Private ls_keyValue as string Private ls_procedure as string Private ls_exitpage as string Private lt_datatable as datatable Private ls_mode as string Protected mytbl as table Public Property Mode as string Get Return Cstr(ViewState("ls_mode")) End Get Set ViewState("ls_mode") = value End Set End Property Public Property ExitPage as string Get Return Cstr(ViewState("ls_exitpage")) End Get Set ViewState("ls_exitpage") = value End Set End Property Public Property t as datatable Get Return lt_datatable End Get Set lt_datatable = value End Set End Property Public Property KeyField as string Get
Benutzerdefinierte Kontrollen 7 Return Cstr(ViewState("ls_keyfield")) End Get Set ViewState("ls_keyfield") = value End Set End Property Public Property KeyValue as string Get Return Cstr(ViewState("ls_keyvalue")) End Get Set ViewState("ls_keyvalue") = value End Set End Property Public Property Procedure as string Get Return Cstr(ViewState("ls_procedure")) End Get Set ViewState("ls_procedure") = value End Set End Property Public Property display as string Get Return Cstr(ViewState("ls_display")) End Get Set ViewState("ls_display") = value End Set End Property Public Property Where as string Get Return Cstr(ViewState("ls_where")) End Get Set ViewState("ls_where") = value End Set End Property Public Property SQL as string Get Return Cstr(ViewState("ls_sql")) End Get Set ViewState("ls_sql") = value
201
ASP.NET 202
7 End Set End Property Public Property ConnStr as string Get Return Cstr(ViewState("ls_ConnStr") ) End Get Set ViewState("ls_ConnStr") = value End Set End Property Protected Overrides Sub CreateChildControls() Dim dv As DataView Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim vSql As string If Where.Length < 1 then vSql = SQL mode = "insert" Else vSql = SQL + Where mode = "update" End If myConnection = New OleDbConnection(ConnStr) myCommand = New OleDbDataAdapter(vSql, myConnection) myCommand.Fill(ds, "vtable") dv = new DataView(ds.Tables("vtable")) Dim Fields As Integer 'Dim t As DataTable t = dv.Table Dim r As DataRow Dim c As DataColumn Dim cell As TableCell Dim row As DataRow Dim Fieldscount As integer Dim s As string Dim vdisplay as string vDisplay = Display + "000000000000000000000000000000000000000000" FieldsCount = 0 s = "Back" me.Controls.Add(new LiteralControl(s)) me.Controls.Add(new LiteralControl("
")) me.Controls.Add(new LiteralControl("
")) If mode = "insert" then me.Controls.Add(new LiteralControl("
Add a New Record:
")) Else me.Controls.Add(new LiteralControl("
Edit Record:
")) End if me.Controls.Add(new LiteralControl("
")) If mode = "update" then For Each r in t.Rows For Each c in t.Columns 'Dieses Feld nicht anzeigen IF vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField then Else me.Controls.Add(new LiteralControl("
")) Dim Box As New TextBox Box.ID = c.ToString me.Controls.Add(box) me.Controls.Add(new LiteralControl("
")) End if FieldsCount = FieldsCount + 1 Next c End If me.Controls.Add(new LiteralControl("
")) me.Controls.Add(new LiteralControl("
")) '--------add button Dim AddButton As New Button if mode = "insert" then AddButton.Text = "Add" else AddButton.Text = "Update" end if AddHandler AddButton.Click, AddressOf AddBtn_Click Me.Controls.Add(AddButton) Dim cancel As New Button cancel.Text = "Cancel" Me.Controls.Add(cancel) End Sub Private Sub AddBtn_Click(Sender As Object, E As EventArgs) 'Erstelle Prozeduraufruf Dim s As String Dim r As DataRow Dim c As DataColumn Dim cell As TableCell Dim row As DataRow Dim column As string Dim Value As string Dim Fieldscount As integer
Benutzerdefinierte Kontrollen 7 Dim vdisplay As string vDisplay = Display + "000000000000000000000000000000000000000" FieldsCount = 0 s = "Execute " + procedure + "" If mode = "update" then For Each r in t.Rows For Each c in t.Columns IF vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField then Else Dim tb As TextBox tb = me.FindControl(c.ToString) column = c.ToString Value = tb.text If c.DataType.ToString = "System.String" Then s = s + " @" + column + "='" + value + "', " Else s = s + " @" + column + "=" + value + ", " End If End IF FieldsCount = FieldsCount + 1 Next c Next r s = s + "@" + KeyField + "=" + KeyValue me.Controls.Add(new LiteralControl(s)) RunSql(s) Else For Each c in t.Columns IF vdisplay.chars(FieldsCount) = "0" or c.ToString = KeyField then Else Dim tb As TextBox tb = me.FindControl(c.ToString) column = c.ToString Value = tb.text If c.DataType.ToString = "System.String" Then s = s + " @" + column + "='" + value + "', " Else
205
ASP.NET 206
7 s = s + " @" + column + "=" + value + ", " End If End IF FieldsCount = FieldsCount + 1 Next c s = s + "@" + KeyField + "=NULL" me.Controls.Add(new LiteralControl(s)) RunSql(s) End if End Sub Sub RunSql(vSql as string) try Dim s As string Dim myConnection As OleDbConnection myConnection = New OleDbConnection(ConnStr) Dim mycommand As New OleDbCommand(vsql,myConnection) myconnection.Open() myCommand.ExecuteNonQuery() myconnection.Close() Catch ex As OleDbException 'SQL-Fehler Dim errItem As OleDbError Dim errString As String Dim s As string For Each errItem In ex.Errors errString += ex.Message + "" Next s = "SQL Error.Details follow:" & errString me.Controls.Add(new LiteralControl(s)) Catch myException as Exception me.Controls.Add(new LiteralControl("Exception: " + myException.ToString())) End try End Sub End Class End Namespace
Benutzerdefinierte Kontrollen 7
Benutzung der benutzerdefinierten GenEditAdd-Kontrolle In diesem Abschnitt erkläre ich Ihnen, wie Sie die GenEditAddKontrolle an ein Datengitter (DataGrid) anhängen und zum Hinzufügen, Editieren oder Löschen von Datensätzen in der Masters-Tabelle (Haupttabelle) benutzen können. Den Code zu diesen Erläuterungen finden Sie auf der CD des Buches. Jedes DataGrid, das diese Kontrolle benutzt, benötigt eine Konfigurationsdatei (config-Datei). In diesem Beispiel wird hierzu die bereits früher im Kapitel diskutierte config_masters.aspx-Datei verwendet. Das DataGrid der Webpage masters.aspx benötigt zwei Hyperlinkspalten für den Add- und den Edit-Modus (Hinzufügen und Editieren). Die Hyperlinks navigieren zur Konfigurationsdatei und geben im Fall des Add-Modus einen Codewert von 0 oder einen gültigen primären Schlüssel im Fall des Editiermodus weiter: Listing 7.12: Masters.aspx
Hier die komplette Codeliste der Masters.aspx-Datei: Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet
207
ASP.NET 208
7 Dim ConnStr As String Dim SQL As String Sub Page_Load(Source As Object, E As EventArgs) ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;" myConnection = New OleDbConnection(ConnStr) if NOT (isPostBack) rebind end if End Sub Sub ReBind() 'DataSet-Befehl SQL = "select m.*, g.code_display as category " SQL = SQL + "from masters m, groups g " SQL = SQL + " where m.code_category = g.code_value" myCommand = New OleDbDataAdapter(SQL, myConnection) 'Benutzen der Fill-Methode des DataSetCommand, um einen Datensatz zu füllen myCommand.Fill(ds, "masters") 'Bindung eines Datengitters Grid1.DataSource=ds.Tables("masters").DefaultView Grid1.DataBind() End Sub Sub RunSql(sql as string) ' Control Validator-Fehler finden if not page.isvalid then response.write("Stored Procedure did not execute") rebind exit sub end if try Dim mycommand2 As New OleDbCommand(sql,myConnection) myconnection.Open() myCommand2.ExecuteNonQuery() myconnection.Close() 'Editiermodus deaktivieren Grid1.EditItemIndex = -1 Catch ex As OleDbException 'SQL-Fehler Dim errItem As OleDbError
Benutzerdefinierte Kontrollen 7 Dim errString As String For Each errItem In ex.Errors errString += ex.Message + "" Next Response.write( "SQL Error.Details follow:" & errString) Catch myException as Exception Response.Write("Exception: " + myException.ToString()) End try rebind End Sub Sub Grid1_delete(sender As Object, e As DataGridCommandEventArgs) Dim code_value As string = Grid1.DataKeys.Item(E.Item.ItemIndex).ToString Dim sql As string sql = "Delete from masters where code_value = " + cstr(code_value) RunSql(sql) End Sub Masters DataGrid 1 Chart of Accounts:
Benutzerdefinierte Kontrollen 7
Zusammenfassung Die in diesem Kapitel erstellte GenEditAdd-Komponente ermöglicht es Ihnen, einem DataGrid Editier- und Eingabe-Funktionalität zu verleihen und damit eine Lücke im Datengitter zu füllen. Diese Kontrolle ersetzt auch den Editiermodus des DataGrid (der nicht automatisiert war und die Programmierung einer Reihe von Events erforderlich machte). Während der Erstellung der Kontrolle wurde die Theorie der Entwicklung benutzerdefinierter Kontrollen erläutert.
211
Business-Objekte
Eine einfache Komponente in C#
218
Aufteilung von Diensten zwischen Webformularen und Komponenten
219
Eine Datenbank-Klasse
221
Zusammenfassung
233
ASP.NET 214
8 Business-Objekte sind eine Bibliothek von Funktionen und Klassen, die in jedem beliebigen Projekt benutzt werden können. Allgemein verwendeter Code wird in einem Business-Objekt eingeschlossen. Dieses Objekt dient als Service-Klasse für ein anderes Objekt. Es wird je nach Bedarf integriert und zerstört, wenn es nicht mehr gebraucht wird. Ein Business-Objekt hat keine Benutzeroberfläche.
Das bin-Verzeichnis Wenn Sie in der Vergangenheit bereits COM-Objekte entwickelt haben, wissen Sie, wie schwer die Registrierung der Komponenten ist. Eine Komponente musste mit Hilfe der regsvr32.exe-Utility registriert werden. Wenn die Komponente modifiziert wurde, musste der ganze Webserver angehalten werden, um die Komponente zu registrieren. ASP.NET hat diesen Prozess vereinfacht. Die Komponenten werden einfach kopiert und in das bin-Verzeichnis eingefügt. Registryupdates sind nicht erforderlich, und um die Komponente zu entfernen, genügt es, diese aus dem bin-Verzeichnis zu löschen. Die ursprünglichen Komponenten können sogar ersetzt werden, während der Webserver in Betrieb ist. ASP.NET ermöglicht es, alle existierenden Anfragen zu komplettieren und alle neuen Anfragen an die neue Komponente weiterzuleiten.
Namensfelder und Zusammensetzungen Entwickler haben die Möglichkeit, interne Codekomponenten zu Namensfeldern (Namespaces) zu gruppieren (Klassen und Benutzeroberflächen). Durch diese logische Organisation wird eine Kollision mit von anderen Entwicklern geschriebenen Klassen verhindert. Ein Namensfeld enthält eine Abkürzung als Bezug zu einem längeren Klassennamen. Die Imports-Direktive (in Visual Basic.NET) und der using-Befehl (in C#) sind Kürzel, die es Ihnen erlauben, Namensfelder zu benutzen, ohne einen kompletten Pfad eingeben zu müssen. Die Metadaten (metadata) eines Objekts zeichnen alle für dessen Verwendung benötigten Informationen auf. Im Allgemeinen enthalten diese Informationen den Namen des Objekts sowie die Namen aller seiner Felder, deren Typ und Details zu allen eingeschlossenen Funktionen einschließlich Art und Name der Parameter.
Business-Objekte 8 Eine Zusammensetzung (Assembly) ermöglicht es, Anwendungen in einer umfassenden Einheit zu bündeln. Der vom .NETCompiler kompilierte Code wird in eine Zwischenform namens IL (Intermediate Language) gebracht. Eine Zusammensetzung enthält alle IL, die Metadaten und andere benötigte Dateien. Jede Zusammensetzung (Assembly) beinhaltet ein Register, das alle Informationen bezüglich seiner Identität (Name und Versionsinformation) und weiterer enthaltener Dateien bietet.
Ein einfaches Business-Objekt in Visual Basic Im Folgenden wird nun ein einfaches Objekt mit einer Eigenschaft und einer Methode erstellt. Der Benutzer spezifiziert die Message-Eigenschaft und die Test-Methode gibt diese an das aufrufende Formular zurück. Der Quellcode für dieses Beispiel befindet sich auf der CD des Buches 1
Erstellung der Komponente Imports System Imports System.Text Imports Microsoft.VisualBasic Namespace BasicObjVb Public Class BasicVb Private ls_message as string Public Sub New() MyBase.New() ls_message = "" End Sub Public Property message as string Get Return ls_message End Get Set ls_message = value End Set End Property Public Function test() as string Dim SB As StringBuilder SB = New StringBuilder(ls_message) SB = SB.Append("..returned from function test") test = SB.ToString()
215
ASP.NET 216
8 End Function End Class End Namespace
Dies Klasse wird BasicVb genannt und befindet sich im Namensfeld BasicObjVb. Sie hat eine Eigenschaft namens Message, die gewöhnlich die Methoden Set und Get sowie eine lokale Variable Is_message enthält. In einer Eigenschaftssyntax wird die Get-Methode verwendet, um die in der lokalen Variablen gespeicherten Werte an das aufrufende Objekt zurückzugeben, während das aufrufende Objekt mit der Set-Methode an diese Eigenschaft sendet. Zu dem Objekt gehört ein Constructor-Event. Dies ist die Subfunktion new(). Das Constructor-Event wird ausgelöst, wenn das Objekt erstellt wird. Die lokale Variable Is_message wird in diesem Event an einem einzigen Platz initiiert. Hinzu kommt noch eine Test-Funktion, die einen String zurückgibt. Ich benutze die StringBuilder-Klasse, um den zurückgegebenen String zu erstellen. Diese Klasse bietet verschiedene Methoden zur Manipulation des Strings. Einige dieser Methoden sind Append, Replace, Remove und ToString (Anhängen, Ersetzen, Entfernen, ToString). Ich weise dem StringBuilder die MessageEigenschaft zu und benutze dann dessen Append-Methode (Anhängen), um einen weiteren String hinzuzufügen. 2
Kompilieren des Objekts
Dies geschieht durch Ausführen des folgenden DOSBefehls: vbc /t:library /out:g:\aspnetsamples\bin\BasicObjVb.dll BasicObj.vb 3
Erstellen eines Webformulars
Erstellen Sie nun ein Webformular, um die Komponente zu testen. Abbildung 8.1 zeigt das Ergebnis:
Business-Objekte 8
217
Listing 8.1: BasicObj.aspx Sub Page_Load(Sender As Object, E As EventArgs) Dim s As string Dim Comp As BasicVb Comp = New BasicVb() Comp.Message = "Hello World" s = Comp.test() response.write(s) End Sub
In diesem Webformular wird das BasicObjVb-Namensfeld mit der Import-Direktive importiert. Dann wird eine Komponente des Typs BasicVb deklariert und Hello World der zugehörigen Message-Eigenschaft zugewiesen. Ich rufe die Test-Funktion auf und erhalte einen String, den ich in der lokalen Variable s speichere. Zum Schluss benutze ich response.write, um den String für den Browser zu rendern.
Ein einfaches BusinessObjekt Abb. 8.1
ASP.NET 218
8
Eine einfache Komponente in C# Als Nächstes werden Sie lernen, das gleiche Objekt in C# zu erstellen. Der Quellcode für dieses Beispiel befindet sich im Unterverzeichnis ..\basic\c auf der CD des Buches. 1
Erstellen der Komponente
Listing 8.2: BasicObjC.cs namespace BasicObjC { using System; using System.Text; public class BasicC{ private String ls_message; public BasicC() { //constructor ls_message = null; } public string message { get { return ls_message; } set { ls_message = value; } } public String test() { StringBuilder SB = new StringBuilder(ls_message); SB.Append(" From test function..."); return SB.ToString(); } } //class } //namespace
Abgesehen von der geänderten Syntax ähnelt die Programmierung eines Objekts in C# dem gleichen Vorgang in Visual Basic. Beachten Sie, dass in diesem Fall
Business-Objekte 8 die öffentliche Methode (public) BasicC() der Constructor ist. Diese öffentliche Methode trägt den gleichen Namen wie die Klasse. 2
Kompilieren des Objekts
Führen Sie die bat-Datei BasicObjC.bat aus, die die folgenden DOS-Befehle enthält: csc /t:library /out:g:\aspnetsamples\bin\BasicObjC.dll BasicObjC.cs
Hierdurch wird die DLL-Datei im bin-Ordner platziert. 3
Erstellen des Webformulars
Erstellen Sie nun ein aspx-Formular, um die Kontrolle zu testen. Listing 8.3: BasicObjC.aspx public void Page_Load(Object sender, EventArgs E) { BasicC comp = new BasicC(); comp.message = "Hello World"; String s = comp.test(); display.InnerHtml = s; } A Simple C# Component Object Output:
Aufteilung von Diensten zwischen Webformularen und Komponenten Die typische Aufgabe eines Webformulars besteht darin, HTML- oder XML-Seiten für den Browser zu rendern. Könnte man die Webformulare vielleicht als Business-Objekte bezeichnen, da sie sich auf dem Server befinden? In einer Umge-
219
ASP.NET 220
8 bung aus Komponenten wäre die Antwort sicherlich negativ. Ein Webformular enthält die Präsentations- und Business-Logik auf der gleichen Seite. Ein Business-Objekt jedoch besitzt keine Präsentationselemente. Man könnte es als ein Objekt betrachten, das Dienstleistungen an ein anderes Formular liefert. Als guter Dienstleister wird es immer dann aufgerufen, wenn es benötigt wird, und anschließend wieder deaktiviert. Diese Service-Analogie ist auch der Grund dafür, dass Business-Objekte als Service-Klassen eingestuft werden. Die Anwendung von Business-Objekten bietet eine Reihe von Vorteilen. Einige davon sind im folgenden Abschnitt aufgeführt: Unterstützt Verkapselung: Sie schließen häufig genutzte Funktionen in ein Objekt ein und zeigen nur die Eigenschaften und Methoden an. Die meisten Webformulare erfordern beispielsweise einen Datenbankzugang und Verarbeitungsroutinen. Anstatt in jeder Seite ein Skript für die Datenbank-Verbindungsroutinen zu schreiben, können Sie ein Business-Objekt erstellen, das Ihnen diese Arbeit abnimmt. Das Objekt zeigt die Eigenschaften und Methoden an und der Benutzer muss nichts über die inneren Arbeitsweisen erfahren. Einfache Wartung: Ein in einem Business-Objekt verkapselter Code ist einfacher zu warten als ein Code, der über mehrere Seiten verteilt ist. Bei Änderungen müssen Sie lediglich das Business-Objekt modifizieren, während der Konsument des Objekts ähnlich wie bei kaskadenartigen Style-Sheets überhaupt nichts tun muss. Wenn Sie beispielsweise die Definition eines H1Tags (Vorzeichen) ändern wollen, müssen Sie dies lediglich an einer Stelle erledigen, da die Änderung anschließend kaskadenartig auf alle Seiten übergeht, die dieses Vorzeichen benutzen. Verbesserung durch wiederholten Gebrauch: Einer der fundamentalsten Vorteile besteht darin, dass der Code durch häufigen Gebrauch und Verfeinerung verbessert wird. Je mehr Benutzer das Objekt testen, desto öfter wird der Debug-Prozess (Fehlererkennung und Behebung) durchlaufen und der Code immer weiter verbessert. Durch die wiederholte Nutzung des Codes
Business-Objekte 8 sind Entwickler in der Lage, sich eine Bibliothek von getesteten und bewährten Funktionen aufzubauen, die sie mit anderen Entwicklern teilen können.
Eine Datenbank-Klasse In diesem Abschnitt werden Sie lernen, wie man eine Datenbank-Klasse erstellt. Die Klasse enthält häufig genutze Funktionen für die Arbeit mit einer Datenbank. Sie enthält Eigenschaften zur Definition eines Datenbank-Verbindungsstrings und gibt dem Objekt ein SQL-Statement, das auf die Datenbank angewendet wird. Das SQL-Statement kann Daten zurückgeben (wie in einem SELECT-Statement) oder Aktionsabfragen wie Aktualisieren, Einfügen und Löschen ausführen und gespeicherte Prozeduren aufrufen, die keine Daten zurückgeben. Die erste Methode gibt eine Datenansicht (DataView) zurück, die zur Bindung einer Kontrolle genutzt werden kann. Die zweite Methode ist eine generische Funktion zur Anwendung von Aktions-SQL-Statements auf Datenbanken. Sie werden lernen, die Klasse zunächst in Visual Basic.NET und dann in C# zu erstellen.
Die Datenbank-Klasse in Visual Basic.NET Beginnen Sie mit der Erstellung der Komponente in Visual Basic.NET. Der Quellcode für dieses Beispiel befindet sich im Unterverzeichnis ...\SQLClass\SqlClassvb auf der CD des Buches. 1
Die SQL-Eigenschaft: Dies ist der SQL-String, der vom Benutzer an dieses Objekt weitergegeben wird. Der String kann aus jedem gültigen SQL-Statement bestehen. Er wird durch die Set- und die Get-Methode definiert und besitzt eine zugewiesene lokale Variable Is_sql. Private ls_sql as string Public Property SQL as string Get Return ls_sql End Get Set ls_sql = value
221
ASP.NET 222
8 End Set End Property 2
Die ConnStr-Eigenschaft: Diese Eigenschaft wird vom Benutzer an den Verbindungs-String weitergegeben. Er wird mit Hilfe der Set- und Get-Methoden und einer lokalen Variablen Is_connstr implementiert. Private ls_ConnStr as string Public Property ConnStr as string Get Return ls_ConnStr End Get Set ls_ConnStr = value End Set End Property
3
Die Funktion Populate: Diese Funktion gibt eine Datenansicht (DataView) zurück, die zur Bindung einer Kontrolle genutzt werden kann. Public Function Populate() As DataView Dim dv As DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet myConnection = New OleDbConnection(ConnStr) myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "vTable") Populate = ds.Tables("vTable").DefaultView End Function
4
Funktion RunSQL: Diese Funktion haben Sie bereits in
einem früheren Kapitel kennen gelernt. Es handelt sich hierbei um eine generische Funktion, die zur Anwendung einer Aktionsabfrage an eine Datenbank genutzt werden kann. Aktionsanfragen geben keine Daten zurück. Sie können diese Funktion auch zur Ausführung gespeicherter Prozeduren, die keine Daten zurückgeben, verwenden, indem Sie diese mit dem execute-Statement aufrufen (execute = ausführen). So
Business-Objekte 8 führt zum Beispiel das Statement Execute p_masters parameter a, parameter b, etc die gespeicherte Prozedur p_masters aus. p_masters könnte zum Beispiel eine Prozedur sein, die eine Zeile in die Haupttabelle einfügt. Function RunSql(vsql as string) as String Dim Message As string try Dim myConnection As OleDbConnection myConnection = New OleDbConnection(ConnStr) Dim mycommand As New OleDbCommand(vsql,myConnection) myconnection.Open() myCommand.ExecuteNonQuery() myconnection.Close() Catch ex As OleDbException Dim errItem As OleDbError Dim errString As String For Each errItem In ex.Errors errString += ex.Message + " " Next Message = "SQL Error.Details follow:" & errString Catch myException as Exception message = "Exception: " + myException.ToString() End try RunSql = message End Function 5
Die Contructor-Funktion: Die Contructor-Funktion wird
bei jeder Initiierung eines Objekts gestartet. Zwei Eigenschaften werden in die freien Felder der Funktion eingefügt. Public Sub New() MyBase.New() ls_sql = "" ls_ConnStr = "" End Sub
Hier kommt die komplette Codeliste der Datenbank-Klasse:
223
ASP.NET 224
8 Listing 8.4: SQLClass.vb Imports System Imports System.Data Imports System.Data.OleDb Imports System.Text Namespace SQLNameSpace Public Class SQLClass Private ls_sql as string Private ls_ConnStr as string Public Sub New() MyBase.New() ls_sql = "" ls_ConnStr = "" End Sub Public Property SQL as string Get Return ls_sql End Get Set ls_sql = value End Set End Property Public Property ConnStr as string Get Return ls_ConnStr End Get Set ls_ConnStr = value End Set End Property Public Function Populate() As DataView Dim dv as DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet myConnection = New OleDbConnection(ConnStr) myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "vTable") Populate = ds.Tables("vTable").DefaultView End Function Public Function test() as string
Business-Objekte 8 Dim SB As StringBuilder SB = New StringBuilder(ls_sql) SB = SB.Append("..returned from function test") test = SB.ToString() End Function Function RunSql(vsql as string) as String Dim Message As string try Dim myConnection As OleDbConnection myConnection = New OleDbConnection(ConnStr) Dim mycommand As New OleDbCommand(vsql,myConnection) myconnection.Open() myCommand.ExecuteNonQuery() myconnection.Close() Catch ex As OleDbException Dim errItem As OleDbError Dim errString As String For Each errItem In ex.Errors errString += ex.Message + " " Next Message = "SQL Error.Details follow:" & errString Catch myException as Exception message = "Exception: " + myException.ToString() End try RunSql = message End Function End Class End Namespace
Kompilieren der Datenbank-Klasse Zur Kompilierung der Datenbank-Klasse in eine DLL steht Ihnen eine bat-Datei zur Verfügung. Die Codeliste der Datei SQLClass.bat sieht folgendermaßen aus: set outdir=g:\aspnetsamples\bin\SQLClass.dll set assemblies=System.dll,System.Web.dll,System.Data.dll,Sys tem.XML.dll vbc /t:library /out:%outdir% /r:%assemblies% SQLClass.vb pause
225
ASP.NET 226
8 Diese Datei kompiliert die SQLClass.dll und platziert diese im bin-Ordner.
Testen der Datenbank-Klasse Das vorgegebene Webformular dient zum Testen der Funktionalität der Datenbank-Klasse. Abbildung 8.2 zeigt das Ergebnis.
Test der DatenbankKlasse Abb. 8.2
Der dazugehörige Code sieht folgendermaßen aus: Listing 8.5: TestVbClass.aspx Dim Comp As SQLClass Dim ConnStr As String Dim SQL As String Sub Page_Load(Source As Object, E As EventArgs) Comp = New SQLClass() Comp.ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;" if NOT (isPostBack) rebind end if
Business-Objekte 8 End Sub Sub Show_Click(Sender As Object, E As EventArgs) Message.Text = "Masters Table Displayed... " ReBind End Sub Sub Insert_click(Sender As Object, E As EventArgs) sql = "Insert into Masters(code_display,code_category,type)" sql = sql + "Values ('test',701,'E')" Comp.RunSql(sql) rebind Message.Text = "Inserted test record... " End Sub Sub Delete_click(Sender As Object, E As EventArgs) sql = "delete from masters where code_display = 'test'" Comp.RunSql(sql) rebind Message.Text = "Deleted all test records..." End Sub Sub Update_Click(Sender As Object, E As EventArgs) sql = "UPDATE Masters Set Opening = 90 WHERE code_display = 'test'" Comp.RunSQL(sql) rebind Message.Text = "Updated all test records: Set closing balance = 90...! " End Sub Sub ReBind() Comp.SQL = "select * from Masters" DataGrid1.DataSource=Comp.populate() DataGrid1.DataBind() End Sub A DataBase Class in Visual Basic 1
Initiierung des Objekts: Das Objekt wird im page_load-
Event des Webformulars initiiert. Der folgende Verbindungsstring wird an das Objekt weitergegeben: Sub Page_Load(Source As Object, E As EventArgs) Comp = New SQLClass() Comp.ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;" if NOT (isPostBack) rebind end if End Sub 2
Die ReBind-Funktion gibt eine SQL-Anfrage an das Objekt weiter und bindet ein DataGrid an die vom Objekt auf folgende Weise zurückgegebene Datenansicht (DataView): Sub ReBind() Comp.SQL = "select * from Masters" DataGrid1.DataSource=Comp.populate() DataGrid1.DataBind() End Sub
3
Das Insert_click-Event führt ein Eingabe-Statement (Insert) in der Datenbank mit Hilfe der RunSQL-Funktion aus. Sub Insert_click(Sender As Object, E As EventArgs) sql = "Insert into Masters(code_display,code_category,type)" sql = sql + "Values ('test',701,'E')"
Business-Objekte 8 Comp.RunSql(sql) ReBind Message.Text = "Inserted test record... End Sub 4
"
Das Update_Click-Event führt ein Update-Statement (Aktualisieren) in der Datenbank mit Hilfe der RunSQLFunktion aus. Sub Update_Click(Sender As Object, E As EventArgs) sql = "UPDATE Masters Set Opening = 90 WHERE code_display = 'test'" Comp.RunSQL(sql) ReBind Message.Text = "Updated all test records: Set closing balance = 90...! " End Sub
5
Das Delete_click-Event führt ein Lösch-Statement (delete) mit Hilfe der RunSQL-Funktion aus. Sub Delete_click(Sender As Object, E As EventArgs) sql = "delete from masters where code_display = 'test'" Comp.RunSql(sql) ReBind Message.Text = "Deleted all test records..." End Su
Die Datenbank-Klasse in C# Der folgende Abschnitt enthält eine komplette Codeliste der Datenbank-Klasse in C#. Abgesehen von der geänderten Syntax gleicht die Klasse der im vorigen Abschnitt beschriebenen Klasse in Visual Basic.NET, so dass der Code hier nicht noch einmal ausführlich erläutert werden muss. Der Quellcode für dieses Beispiel befindet sich im Unterverzeichnis ...\Sql\Class\SqlClassC auf der CD des Buches.
229
ASP.NET 230
8 Listing 8.6: SQLClassC.cs namespace SQLNameSpaceC { using System; using System.Data; using System.Data.OleDb; using System.Text; public class SQLClassC { private String ls_connStr; private String ls_sql; public String ConnStr { get { return ls_connStr; } set { ls_connStr = value; } } public String SQL { get { return ls_sql; } set { ls_sql = value; } } public DataView Populate() { //for queries that return data //and for binding controls OleDbConnection myConnection = new OleDbConnection(ConnStr); OleDbDataAdapter myCommand = new
Business-Objekte 8 OleDbDataAdapter(SQL, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "vTable"); return ds.Tables["vTable"].DefaultView; } public String RunSQL( string vsql) { try { OleDbConnection myConnection = new OleDbConnection(ConnStr); OleDbCommand mycommand = new OleDbCommand(vsql, myConnection); myConnection.Open(); mycommand.ExecuteNonQuery(); myConnection.Close(); } catch(Exception e) { string ret = "Exception: " + e.ToString() ; } return("OK"); } public String test() { StringBuilder SB = new StringBuilder(ls_connStr); SB.Append(" ," + ls_sql); return SB.ToString(); } } }
Kompilieren der C#-Klasse Die Datenbank wird auf folgende Weise zu einer DLL kompiliert und im bin-Ordner platziert:
231
ASP.NET 232
8 Listing 8.7: SQLClassC.bat set outdir=g:\aspnetsamples\bin\SqlClassC.dll set assemblies=System.dll,System.data.dll csc /t:library /out:%outdir% /r:%assemblies% SqlClassC.cs pause
Testen der C#-Klasse Dieses Webformular testet die Methoden Populate und RunSQL der Datenbank-Klasse. Listing 8.8: TestcClass.aspx SQLClassC comp = new SQLClassC(); public void Page_Load(Object sender, EventArgs E) { comp.SQL = "Select * From Masters"; comp.ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;"; bind(); string sql = "Insert into Masters(code_display,code_category,type)"; sql = sql + "Values ('test',701,'E')"; comp.RunSQL(sql); } public void Insert_Click(Object sender, EventArgs E) { string sql = "Insert into Masters(code_display,code_category,type)"; sql = sql + " Values ('test',701,'E')"; comp.RunSQL(sql); bind(); } public void Update_Click(Object sender, EventArgs E) { string sql = "UPDATE Masters Set Opening = 90 WHERE code_display = 'test'"; comp.RunSQL(sql);
Business-Objekte 8 bind(); } public void Delete_Click(Object sender, EventArgs E) { string sql = "delete from masters where code_display = 'test'"; comp.RunSQL(sql); bind(); } public void bind() { DataGrid1.DataSource=comp.Populate(); DataGrid1.DataBind(); } SQL Class in C#
Zusammenfassung Die Entwicklung von Business-Objekten ist ein wichtiges Thema. Ein gut eingebundener Code ist leicht zu warten, verbessert sich bei wiederholter Anwendung und kann ganz leicht von verschiedenen Entwicklern gemeinsam genutzt werden. Daher sind Business-Objekte ein praktischer Weg zur Trennung von Präsentation und Code. Funktionen zur Manipulation von Datenbanken sind hervorragend für die Einbindung in Business-Objekte geeignet. In diesem Kapitel wurden generische Funktionen zur Anwendung von SQL-Statements und Anfragen in Datenbanken entwickelt. Diese Funktionen sind in der Lage, Daten zurückzugeben,
233
ASP.NET 234
8 Aktionsanfragen wie Eingabe, Update und Löschen (Insert, Update und Delete) anzuwenden und gespeicherte Prozeduren auszuführen.
Arbeiten mit ASP.NET Webdiensten
Erstellen eines einfachen Webservice
238
Erstellen eines Webservice mit Visual Studio
252
Aufruf des Webservice aus einem Webformular
258
Nutzung des Webservice-Verhaltens zur Erstellung von Funktionsaufrufen
262
Zusammenfassung
268
ASP.NET 236
9 Entwicklung von Anwendungen bedeutet heute: Entwicklung für das Web. Das Ziel ist die Entwicklung von Serveranwendungen, die auf jedem Betriebsystem, das HTTP-Anwendungen verarbeiten kann, laufen. Hierfür sind zwei Technologien immer wichtiger geworden: SOAP und XML. Die Funktionalität eines Business-Objekts kann mit einem SOAP-Protokoll genutzt und als XML-Daten ausgetauscht werden. Die Webservices machen all dies möglich. Einen Webservice kann man am einfachsten als eine Bibliothek nützlicher Funktion, die in einem Business-Objekt eingeschlossen sind, beschreiben. Auf diese Funktionen kann mit SOAP und XML zugegriffen werden. Auch HTTP, Get und Post können zur Datenübertragung genutzt werden, jedoch sind diese Funktionen auf das Senden und Empfangen von Daten/Werte-Paaren oder Namen beschränkt. SOAP dagegen kann zur Serialisierung von komplexen Strukturen wie ASP.NET-Datensätzen, komplexen Arrays, benutzerdefinierten Typen und XML-Knoten verwendet werden. SOAP ist der Standard für Funktionsaufrufe zwischen Maschinen in XML. Der Austausch von Informationen in XML ermöglicht es einer Benutzeranwendung, ungeachtet des Betriebssystems, des Komponentenmodells, das von beiden unterstützt wird, oder der Sprache, in der die Anwendung programmiert wurde, eine Serverfunktion aufzurufen. Der Grund dafür ist, dass XML im Grunde nur eine Textdatei ist, die von allen Maschinen verstanden wird, und dass SOAP HTTP, das am weitesten verbreitete Internet-Übertragungsprotokoll, welches im Wesentlichen von allen Browsern benutzt wird, verwendet. Die Kommunikation zwischen verschiedenen Anwendungen ist keine wirklich neue Idee. Es gibt bereits Technologien wie DCOM, RPC und MSMQ (Microsoft Message Queue Service), die diese Art der Kommunikation unterstützen. Die Haupteinschränkung dieser Technologien besteht darin, dass nur Kommunikation zwischen gleichen Systemen unterstützt wird. Ein DCOM-System arbeitet recht gut in reinen Microsoft-Systemen und benötigt einen DCOM-Server sowie einen DCOM-Client. Diese Technologien erfordern die Anschaffung einer bestimmten Architektur sowie die Bindung an dieselbe. Ein weiterer Nachteil besteht darin, dass diese Technologien Probleme haben, die Firewall-Programme anderer Unternehmen zu überwinden. SOAP jedoch benutzt HTTP und kann diese Programme daher umgehen.
Arbeiten mit ASP.NET Webdiensten 9 Webservices sind ein leicht verständliches Konzept. Ein Business-Objekt wird als Service bezeichnet. Es handelt sich hierbei um ein nicht sichtbares Objekt, das eine Reihe von nach einer logischen Klassifizierung gruppierten Funktionen enthält. Die Bezeichnung Service wurde gewählt, weil dieses Objekt versucht, einem anderen Objekt zu dienen. Dieses andere Objekt kann ein HTML-Formular, ein Webformular oder jede andere Funktion sein, die das Service-Objekt initialisieren kann. Als guter Dienstleister bleibt es so lange aktiv, wie es von einem aufrufenden Objekt benötigt wird, und wird dann wieder deaktiviert. In ASP.NET programmieren Sie einen Webservice wie ein normales Business-Objekt, mit dem einzigen Unterschied, dass hierbei den Funktionen ein spezielles -Attribut vorangestellt wird, das diese als Webservice kennzeichnet. Webservices können eine Bibliothek mit Anwendungen für Dritte sein, die über das Internet aufgerufen werden können. Beispiel hierfür sind die Verfolgung von Transporten (ähnlich der auf der FedEx-Site verfügbaren Funktionalität), Kreditkartenüberprüfung oder Kontrolle der richtigen Schreibweise. Webservices eröffnen einige aufregende Möglichkeiten gegenüber der traditionellen Art der Anwendungsentwicklung. Mit Hilfe von ASP.NET und Webservices können Sie Anwendungen entwickeln, die Daten über das Internet und HTTP senden und empfangen können. Denken Sie nur an Buchhaltungsanwendungen. Traditionelle Anwendungen dieser Art wurden auf der Basis des Client/Server-Modells entwickelt. Die Module (Finanzbuchhaltung, Inventur, Fakturierung usw.) teilen sich eine gemeinsame Datenbank und sind über eine Art Netzwerkprotokoll miteinander verbunden. Bei der Benutzung von Webdiensten müssen die einzelnen Module nicht mehr miteinander verkabelt sein. Nehmen Sie als Beispiel ein Produktionsunternehmen, dessen Lager und Finanzabteilung sich in unterschiedlichen Bundesländern befinden. Da die Daten des Bestands und der Finanzbuchhaltung integriert werden müssen, wird eine zentrale Datenbank benutzt. In einer traditionellen Client/Server-Umgebung wären die Inventur- und die Finanzbuchhaltungs-Software sowie die zentrale Datenbank über eine Art Satellitenverbindung aneinander gekoppelt. Im WebserviceModell jedoch wird das Internet (HTTP) als Verbindung ge-
237
ASP.NET 238
9 nutzt. Daten würden über das Internet (HTTP) durch das XMLoder das SOAP-Protokoll ausgetauscht. Beide Module könnten in verschiedenen Programmiersprachen geschrieben werden und auf unterschiedlichen Betriebssystemen laufen. Da beide Module XML und SOAP verstehen, könnten sie auf eine zentrale Sammlung von Funktionen zugreifen, die dann mit der Datenbank interagieren.
Erstellen eines einfachen Webservice Zur Demonstration der damit verbundenen Technologien habe ich einen Webservice mit zwei einfachen Funktionen namens TestFunction() und add() erstellt. Die Funktion TestFunction() gibt einen auf der an ihn weitergegebenen BooleanVariablen basierenden Nachrichtenstring zurück. Die Funktion add() gibt die Summe zweier an sie weitergegebener ganzer Zahlen zurück. Um den Service zu erstellen, habe ich einen einfachen Text-Editor benutzt und die Datei mit der Erweiterung .asmx gespeichert. Die Quelldatei BasicService.asmx findet sich im Unterverzeichnis ...\Basic\getPost des Musterordners für dieses Kapitel. Listing 9.1: BasicService.asmx Imports System Imports System.Web.Services Public Class TestService: Inherits WebService Public Function TestFunction (vInput as Boolean) As String If (vInput = TRUE) Then TestFunction = "It is the truth..." Else TestFunction = "False!False!False" End if End Function Public Function add( a as integer, b as integer) as string add = cstr(a+b) End function End Class
Das WebService-Attribut weist ASP.NET an, den Code als Webservice darzustellen und die Sprachdirektive wählt Visual
Arbeiten mit ASP.NET Webdiensten 9 Basic.NET als Kompiliersprache. Das Klassenattribut spezifiziert die Klasse, welche die eingehenden Funktionsaufrufe bearbeitet. In diesem Fall handelt es sich dabei um die TestService-Klasse. Die Namensfelder (Namespaces) System und System.Web enthalten vorgefertigte Optionen, die im Webservice benötigt werden. Diese werden daher mit der Import-Direktive importiert. Die TestService-Klasse entsteht aus der WebService-Klasse. Eine solche inhärente Klasse kann die Funktionaliät der Klasse, aus der sie entstanden ist, übersteuern bzw. erweitern. Wenn Sie ein Namensfeld (Namespace) importieren, können Sie, im Gegensatz zur Arbeit mit inhärenten Namensfeldern, die Funktionalität der importierten Klasse nicht erweitern. Der neuen Klasse TestService werden nun zwei Funktionen (TestFunction und Add) hinzugefügt. Der Prozess der Funktionsdefinition unterscheidet sich nicht von der entsprechenden normalen Visual Basic Syntax. Dennoch wird hier den Methoden, die dem Webservice zur Verfügung gestellt werden sollen, ein neues Attribut hinzugefügt. ASP.NET stellt alle Methoden mit diesem Attribut als Webservices dar.
Testen des Service ASP.NET bietet eine vorgefertigte Unterstützung für den Test der Funktionalität des Webservice. Wenn Sie die Datei TestService.asmx in Ihren Browser mit IIS öffnen (ein vollständiger URL, der über einen Localhost läuft), sehen Sie die Seite so, wie sie in Abbildung 9.1 gezeigt wird. Diese Seite liest die asmx-Datei und bietet die Möglichkeit, alle als WebMethods gekennzeichneten Funktionen zu testen. Um die TestFunction- oder die Add-Methode zu testen, müssen Sie lediglich die geforderten Parameter eingeben. Das Ergebnis der Funktion wird dann als XML über das GET-Protokoll angezeigt.
Der WDSL-Vertrag Der Web Services Description Language-Vertrag (Contract) (oder kurz WDSL) ist eine XML-basierte Datei mit der kompletten Beschreibung eines Webservice. Er entspricht der Typenbibliothek eines COM-Objekts. Eine Typenbibliothek enthält Informationen bezüglich der spezifischen Kennzeichnung einer Komponente (CLSID), der von ihr implementierten Benut-
239
ASP.NET 240
9 zeroberflächen sowie deren Methodensignatur. Auf ähnliche Weise zeigt die WSDL-Datei (oder der Vertrag/Contract) alle in einem Webservice enthaltenen Methoden, die von diesen erwarteten Parameter und die unterstützten Protokolle an. Eine Typenbibliothek kann nur in Microsoft-Systemen verwendet werden, während der WSDL-Vertrag (Contract) als XML-Datei auch in anderen Systemen benutzt werden kann.
Der Service im Test Abb. 9.1
Diese Datei ist ein Vertrag mit der Außenwelt, der spezifiziert, welche Fähigkeiten der Webservice hat. Wenn Sie sich die Datei TestService.asmx mit IIS anschauen, können Sie auf den Hyperlink Service Description klicken, um die XML-Datei zu betrachten, die den Vertrag enthält. Den gleichen Effekt erzielen Sie, wenn Sie einen ?WSDL-String an den kompletten URL der asmxDatei anhängen (Beispiel: http://localhost/TestService.asmx? WSDL). Die WSDL-Datei bestimmt, welche Protokolle dem aufrufenden Client zur Verfügung gestellt werden, und liefert Informationen darüber, wie die Funktion aufgerufen werden soll. Die WSDL-Datei für die Methoden TestFunction und Add beinhaltet Abschnitte für den Zugriff auf einen Webservice mit SOAP-, HTTP Post- oder HTTP Get-Protokollen. Des Weiteren bestimmt die Datei, wie eine Funktion aufgerufen wird und welche Parameter hierfür eingegeben werden müssen.
Arbeiten mit ASP.NET Webdiensten 9 Obwohl eine detaillierte Kenntnis des WSDL-Vertrags nicht erforderlich ist, sollen hier doch die wichtigen Elementmarkierungen (tags) analysiert werden.
Jede Methode des Webservice ist gleichzeitig auch eine Operation desselben. Dies gilt auch für die Methoden Add und TestFunction. Entsprechend der WSDL-Spezifikation ist eine Operation eine abstrakte Beschreibung einer vom Service unterstützten Aktion.
Dies ist das Grundelement des WSDL-Dokuments. Alle anderen Elemente befinden sich innerhalb seiner Markierungen (tags). Seine Attribute bestimmen das Zielnamensfeld und andere Definitionen desselben.
Diese Elementmarkierung (element tag) bestimmt den Namen des Webservice, den der WSDL-Vertrag beschreibt. Der hier gerade erstellte Webservice befindet sich in der TestService-Klasse. Das -Attribut für diesen Webservice ist der Klassenname, wie im folgenden Auszug aus dem WSDL-Vertrag zu erkennen ist:
Innerhalb der -Elementmarkierung (Element tag) spezifiziert der WSDL-Vertrag verschiedenen Ports, über die auf den Webservice zugegriffen werden kann. Die Adresse des Webservice wird hier als vollständiger URL angegeben. Die Position des Webservice auf der lokalen Maschine wird folgendermaßen angegeben: http://localhost/AspNetSamples/9_Samples/Basic/GetPost/B asicService.asmx
241
ASP.NET 242
9 Der relevante Auszug aus dem WSDL-Vertrag für das SOAPProtokoll sieht folgendermaßen aus:
Der Webservice kann durch mehrere Ports zugänglich gemacht werden. Auf diesen Webserver kann über SOAP, HTTP GET und HTTP Post zugegriffen werden. Wenn Sie sich den WSDL-Vertrag anschauen, werden Sie feststellen, dass die Elementmarkierung (Element tag) der oben aufgeführten Elementmarkierung ähnelt. Auf dem Rechner sieht der Code so aus:
Die Kommunikation mit einem Webserver beinhaltet auch das Erstellen einer Anfrage (function call/Funktionsaufruf) und den Erhalt einer entsprechenden Antwort. Aus diesem Grund verfügt jede der im WSDL-Vertrag beschriebenen Funktionen für jedes Protokoll die Elementmarkierungen In (Anfrage) und Out (Antwort). So hat die Add-Methode (Hinzufügen) insgesamt sechs Elementmarkierungen (Element tags) (je zwei Markierungen für SOAP, HTTP GET und HTTP Post). Die Elementmarkierung entspricht dem Parameter oder dem von dem Fernprozeduraufruf zurückgegebenen Wert. Der folgende Auszug zeigt die von der Add-Methode (Hinzufügen) bei Verwendung des HTTP GET-Protokolls erwarteten Parameter:
Arbeiten mit ASP.NET Webdiensten 9
Die Elementmarkierung benötigt die Elementmarkierung , um die Eingabe- und Ausgabemeldungen zu identifizieren:
Zum guten Schluss muss der WSDL-Vertrag bestimmen, in welchem Code der Webservice die Daten erwartet. So erfordert zum Beispiel die TestFunction einen Boolean-Parameter (vInput) der als -1, 1 oder True dargestellt werden kann. Die Elementmarkierung legt fest, dass die SOAP-Spezifikationen, welche die folgenden vordefinierten Regeln für solche Situationen enthalten, beachtet werden müssen:
Service-Aufruf mit HTTP GET In diesem Abschnitt werden Sie das HTTP GET-Protokoll benutzen, um den Webservice aufzurufen. Betrachten Sie den WSDL-Teil, der das HTTP GET-Protokoll für die Funktion TestFunction beschreibt. Hier ist der entsprechende Auszug:
243
ASP.NET 244
9 Listing 9.2: WSDL-Auszug für HTTP Get
Die Testfunktion benötigt einen einzigen Parameter namens vInput. Während die Anfrage als String erfolgen muss, wird die Antwort in XML zurückgegeben. Die Anfrage wird erstellt, indem man dem vollständigen Namen des Webservice ein Fragezeichen und den Funktionsnamen zusammen mit den erforderlichen Parametern anhängt: http://localhost/path name/basicservice.asmx/TestFunction?vInput=TRUE
Der Quellcode für dieses Beispiel (basicHTTPGet.html) findet sich im Beispielordner ...\basic\GetPost für dieses Kapitel. Abbildung 9.2 zeigt einen Musteraufruf an die TestFunction-Methode.
Anfrage an einen Webservice mit dem HTTP GET«-Protokoll Abb. 9.2
Arbeiten mit ASP.NET Webdiensten 9 Listing 9.3: basicHTTPGet.html HTTP GET Example WSDL Contract TestFunction?vInput=TRUE TestFunction?vInput=False
Ein Klick auf einen der Funktionslinks ruft die TestFunction auf und gibt den entsprechenden Eingabeparameter weiter. Klicken Sie beispielsweise auf den Link vInput="True", wird True an die TestFunction weitergegeben. Der Webservice gibt die folgende Antwort in XML zurück: It is the truth...
Service-Aufruf mit HTTP Post Dieser Abschnitt beschreibt den Aufruf eines Webservice mit dem HTTP Post-Protokoll. Betrachten Sie den WSDL-Teil, der das HTTP Post-Protokoll für die Funktion TestFunction beschreibt. Hier ist der entsprechende Auszug: Listing 9.4: WSDL-Auszug für HTTP Post
245
ASP.NET 246
9
Um die TestFunction mit dem HTTP Post-Protokoll aufzurufen, müssen Sie den Parameter vInput über eine Eingabe-Kontrolle (input control), die sich innerhalb der Formularmarkierungen (form tags) befindet, weitergeben. Abbildung 9.3 zeigt ein Beispiel-Webformular, das diese Anfrage ausführt. Den entsprechenden Code finden Sie im Anschluss an die Abbildung. Beachten Sie, dass das -Attribut der -Markierung so modifiziert werden muss, dass es auf Ihren Anwendungsordner gerichtet ist. Der Quellcode befindet sich im Unterverzeichnis ...\basic\GetPost im Beispielordner für dieses Kapitel.
Aufruf des Webservice mit dem HTTP PostProtokoll Abb. 9.3
Listing 9.5: basicHTTPPost.html HTTP Post Example
True False
Service-Aufruf mit SOAP SOAP ist das wichtigste Übertragungsprotokoll. Sie werden festgestellt haben, dass die Get- und Post-Protokolle auf das Empfangen und Senden von Daten- oder Namenspaaren beschränkt sind. SOAP ist wichtig, weil es Ihnen die Möglichkeit gibt, komplexe Strukturen wie DataSets, komplexe Arrays, benutzerdefinierte Typen und XML-Knoten zu serialisieren. Ein Clientaufruf an SOAP erfolgt durch das Senden einer Nachricht in der XML-Sprache von SOAP als HTTP-Übertragung. Die äußere Hülle dieser Nachricht beinhaltet eine Kopfzeile und einen Hauptteil. Der Hauptteil beinhaltet den Funktionsnamen und die an die Funktion weiterzugebenden Parameter. Jedem dieser Elemente ist ein XML-Namensfeld (Namespace) vorangestellt (http://schemas.xmlsoap.org/soap/envelope). Eine SOAP-Anfrage an die TestFunction-Funktion könnte folgendermaßen aussehen: True
247
ASP.NET 248
9
Die SOAP-Anfrage wird an einen SOAP Listener auf dem Server geschickt. Dieser Zuhörer ist im Allgemeinen das Webformular. Die Kommunikation zwischen Client und Server erfolgt über HTTP, obwohl auch Transfermechanismen wie etwa Steckdosen, Message Queuing oder E-Mail verwendet werden könnten. Der SOAP Listener führt die aufgerufenen Funktion aus und gibt die Antwort in XML zurück. Eine erfolgreiche Antwort ist in einer Elementmarkierung eingeschlossen, die den Originalnamen der Methode trägt und der die Antwort angehängt ist. Die Antwort an die TestFunction-Funktion wäre daher in der Elementmarkierung TestFunctionResponse eingeschlossen. Der SOAP-Hauptteil der Antwort könnte folgendermaßen aussehen: it is the truth
Glücklicherweise müssen Sie sich nicht mit all den Einzelheiten der Erstellung einer Kommunikationsinfrastruktur für SOAP und XML befassen. ASP.NET nimmt sich dieser Aufgabe an. Sie generieren lediglich ein Proxy Ihres Webservice unter Zuhilfenahme der wsdl.exe-Utility, die in ASP.NET enthalten ist. Dieses Tool liest die WSDL-Datei und generiert einen Proxy, der eine Sprache nach Ihrer Wahl einsetzt. Wann immer ein Client eine Prozedur aufruft, generiert der Proxy eine HTTP-Anfrage und sendet diese an den Server. Anschließend sendet er die erhaltene Antwort zurück an den Client. Sie werden nun lernen, das SOAP-Protokoll in ASP.NET zu implementieren. Das Muster für dieses Beispiel befindet sich im Unterverzeichnis ..../basic/soap des Beispielordners auf der CD des Buches.
Arbeiten mit ASP.NET Webdiensten 9 1
Erstellen Sie die Webservice-Datei. Ich habe bereits die Datei BasicService.asmx erstellt und kopiere sie nur noch in den Ordner.
2
Erstellen Sie die WSDL-Datei. Öffnen Sie BasicService.asmx über IIS (Beispiel: http://localhost/yourvirtual directory/BasicService.asmx). Klicken Sie auf den Hyperlink Service Description. Speichern Sie die daraus resultierende Datei als basicService.wsdl ab. Sie können das gleiche Ergebnis erreichen, wenn Sie zu http://localhost/your virtual directory/BasicService. asmx?wsdl browsen.
ACHTUNG Sie müssen die WSDL-Datei neu erstellen, auch wenn diese Datei bereits im Beispielordner existiert. Diese Datei bezieht sich auf den Anwendungsordner auf meinem Rechner. Sie müssen diese Datei daher durch eine neue WSDL-Datei, die sich auf Ihren Anwendungsordner bezieht, ersetzen.
3
Starten Sie die Datei mbasicService.bat. Diese batDatei hat zwei Aufgaben. Zunächst bringt sie einen Proxy dazu, die Befehlszeilen-Utility (Command-line Utility) wsdl.exe zu nutzen, die in NETSDK enthalten ist. Sie liest die Beschreibung des Webservice aus der WSDL-Datei und erstellt eine Proxy-Klasse mit der Erweiterung .vb (da Sie mit Visual Basic arbeiten). Der Name dieser Klasse ist in der asmx-Datei spezifiziert. Daher heißt diese Proxy-Klasse TestService.vb. Kompilieren Sie dann diese Proxy-Klasse und speichern Sie die daraus resultierende DLL (BasicService.dll) im binOrdner. Wenn Sie alles richtig gemacht haben, sollten jetzt zwei neue Dateien erstellt worden sein: die Proxy-Datei TestService.vb (im lokalen Ordner) und die Datei BasicService.dll (im bin-Ordner). Visual Studio automatisiert die Erstellung des Proxy. Später in diesem Kapitel werden Sie mehr über die Erstellung eines
249
ASP.NET 250
9 Webservice mit Visual Studio erfahren. Abbildung 9.4 zeigt das Ergebnis zu diesem Zeitpunkt der Entwicklung: Listing 9.6: mbasicService.bat REM ---------Make Proxy---------------wsdl.exe /l:VB /n:NameSpaceHersh /out:TestService.vb BasicService.Wsdl REM -----------Compile Proxy-------------Rem Remember to change outdir variable to point to your bin folder set outdir=g:\AspNetSamples\bin\BasicService.dll set assemblies=System.dll,System.Web.dll,System.Data. dll, System.Web.Services.dll,System.Xml.dll vbc /t:library /out:%outdir% /r:%assemblies% TestService.vb
Kompilieren des Proxy Abb. 9.4
4
Testen Sie den Service. Hierzu steht Ihnen ein einfaches Webformular zur Verfügung. Dieses Formular ruft die TestFunction-Funktion auf und gibt den Parameter true an den Webservice. Der zurückgegebene Wert der Funktion wird auf dem Bildschirm angezeigt, wie in Abbildung 9.5 zu sehen ist.
Arbeiten mit ASP.NET Webdiensten 9
Test mit dem SOAPProtokoll Abb. 9.5
Listing 9.7: basicSoap.aspx Protected Sub Page_Load(Src As Object, E As EventArgs) Dim t As New NameSpaceHersh.Testservice Dim s As string s = t.testfunction(true) message.text = "Return from function : " + s End Sub Testing Soap Proxy
251
ASP.NET 252
9
Erstellen eines Webservice mit Visual Studio Die Erstellung eines Webservice mit Visual Studio.NET (kurz VS.NET) beinhaltet die Erstellung eines neuen Webservice-Projekts und die Hinzufügung von Methoden und Eigenschaften zum Formular einer Webservice-Klasse (z.B. ein asmx-Formular). In diesem Abschnitt werden Sie einen Webservice mit Visual Studio.NET erstellen und den Gebrauch des intergrierten VS-Fehlerbehebungsprogramms (Debugger) erlernen. Im Anschluss daran werden Sie ein Visual Studio.NET-Projekt entwickeln, um den hier erstellten Webservice zu benutzen. Der Quellcode für dieses Beispiel befindet sich im VSServiceUnterverzeichnis des Beispielordners für dieses Kapitel. Um die Dateien auf Ihrem System zu installieren, erstellen Sie ein neues Webservice-Projekt und importieren die Beispieldateien dort hinein. Hierdurch ensteht der virtuelle Anwendungsordner auf Ihrem Computer.
Neues Projekt Abb. 9.6
1
Starten Sie Visual Studio.NET.
2
Wählen Sie Datei/Neu/Projekt (File/New/Project) wie in Abbildung 9.6 gezeigt.
Arbeiten mit ASP.NET Webdiensten 9 3
Wählen Sie Visual Basic Projects aus der linken Leiste und Web Services aus der rechten Leiste. Abbildung 9.7 zeigt das Ergebnis
Neuer Webservice Abb. 9.7
4
Geben Sie VSService als Name ein. Unter Umständen wird Ihnen eine Dialogbox Web Access Failed (Webzugang fehlgeschlagen) wie in Abbildung 9.8 angezeigt. Klicken Sie auf OK. Visual Studio.NET nutzt den Dateizugang, um die Lösung (Solution) zu öffnen.
5
Visual Studio.NET generiert eine neue Solution (Lösung), die einen Referenzordner und vier Dateien enthält. Die Datei web.config ist eine XML-Datei, die verschiedene Konfigurationsoptionen (beispielsweise Session timeout interval) enthält und den Webservice während des gesamten Ablaufs überwacht. Die Datei .disco ist eine XML-Datei, die vom Client zur dynamischen Entdeckung von Webdiensten genutzt wird. Wenn Sie diesen Service in einem anderen Projekt nutzen möchten, navigieren Sie zu dieser Datei und VS.NET fügt ihr eine Referenz bezüglich des Service hinzu.
253
ASP.NET 254
9 6
Klicken Sie mit der rechten Maustaste auf Service1. asmx und wählen Sie Öffnen (Open). Wenn Sie die Datei mit Notepad öffnen, werden Sie bemerken, dass diese Datei einen Code aufruft, der sich in einer anderen Datei befindet (Service1.vb):
Die Web Access FailedDialogbox Abb. 9.8
Die Solution-Leiste mit den vier vorinstallierten Dateien Abb. 9.9
Fügen Sie die zwei Funktionen TestFunction() und add() innerhalb des Klassenmoduls wie folgt hinzu: Public Function TestFunction (vInput as Boolean) As String If (vInput = TRUE) Then TestFunction = "It is the truth..." Else TestFunction = "False!False!False" End if End Function Public Function add( a as integer, b as
Arbeiten mit ASP.NET Webdiensten 9
255
integer) as string add = cstr(a+b) End Function
Abbildung 9.10 zeigt, wie die Codeansicht von VS.NET zu diesem Zeitpunkt aussieht.
Erstellen des Webservice Abb. 9.10
Erstellen der Lösung Abb. 9.11
ASP.NET 256
9 7
Klicken Sie mit der rechten Maustaste auf die Lösung (Solution) VSService und wählen Sie Erstellen (Build), wie in Abbildung 9.11 gezeigt. (Sie können auch die Taste % drücken.)
8
Testen Sie den Service durch einen Rechtsklick auf Service1.asmx und wählen Sie View in Browser (Im Browser öffnen). Sie sehen die in Abbildung 9.12 gezeigte voreingestellte Testseite, deren Funktionen Sie jetzt durch Eingabe der geforderten Parameter testen können.
9
Visual Studio.NET enthält ein effektives Fehlerbehebungsprogramm (Debugger). Sie können Break-Punkte setzen und sich durch Drücken der Taste ( durch den Code bewegen. Um das Fehlerbehebungsprogramm zu nutzen, öffnen Sie die Datei Service1.asmx und setzen Sie einen Break-Punkt durch Doppelklick auf die graue Leiste auf der linken Seite außerhalb des Codebereichs. Ein roter Punkt erscheint an dieser Stelle, wie Sie in Abbildung 9.13 sehen können. Starten Sie den Fehlerbehebungsmodus (Debug-Mode) durch Drücken der Taste %. Wenn Sie nach einem Debug-Ordner gefragt werden, wählen Sie irgendeinen Ordner, der sich nicht in wwwroot befindet (z.B. c:\test). Die voreingestellte Testseite wird nun angezeigt. Geben Sie die Parameter der Funktion ein, die Sie nach Fehlern durchsuchen möchten. Der Debugger bringt Sie zurück zu der Linie des Break-Punktes. Wenn Sie den Mauszeiger auf einen Parameter ziehen und dort für eine Sekunde halten, sehen Sie, wie der Parameter weitergegeben wird, wie in Abbildung 9.14 zu erkennen ist. Drücken Sie die Taste (, um sich schrittweise durch den Code zu bewegen.
Arbeiten mit ASP.NET Webdiensten 9
257
Testen der Funktionen Abb. 9.12
Setzen eines Break-Punktes Abb. 9.13
10
Browsen Sie zum bin-Ordner im VSService-Ordner. Hier finden Sie eine kompilierte DLL namens VSService.dll. Visual Studio.NET hat den Proxy erstellt und für Sie kompiliert.
ASP.NET 258
9
Benutzen des Debuggers Abb. 9.14
Aufruf des Webservice aus einem Webformular In diesem Abschnitt werden Sie eine Clientanwendung erstellen, die den im vorigen Abschnitt erstellten Webservice nutzt. Es handelt sich dabei um eine VS-Webanwendung, deren Erstellung in den folgenden Schritten erklärt wird. 1
Starten Sie Visual Studio.NET
2
Wählen Sie Datei/Neu/Projekt (File/New/Project).
3
Wählen Sie Visual Basic Projects aus der linken Leiste und Asp.NET Web Application aus der rechten Leiste, wie in Abbildung 9.15 gezeigt. Geben Sie VSClient als Projektnamen ein.
4
Ziehen Sie drei Textboxen, drei Label und einen Button in das Formular. Klicken Sie mit der rechten Maustaste auf einen freien Bereich des Formulars und wählen Sie Eigenschaften (Properties). Vergewissern Sie sich, dass die Page Layout-Eigenschaft auf GridLayout eingestellt ist. Mit GridLayout können Sie die Kontrollen verschie-
Arbeiten mit ASP.NET Webdiensten 9
259
ben und sichtbar auf dem Formular platzieren. Ändern Sie die Text-Eigenschaften von drei Labeln auf das Lesen von Parameter A und B respektive auf das Ergebnis (Result). Stellen Sie die ID-Eigenschaften der drei Textboxen auf das Lesen der Parameter 1 und 2 respektive auf Ergebnis hinzufügen (ResultAdd). Das Formular sollte jetzt der Abbildung 9.16 entsprechen.
Die neue Projektleiste Abb. 9.15
Design des Webformulars Abb. 9.16
ASP.NET 260
9 5
Hinzufügen einer Webreferenz Abb. 9.17
Sie müssen nun noch einen Bezug zum VSService herstellen, um diesen in der Anwendung nutzen zu können. Wählen Sie hierzu Project/Add Web Reference aus dem Hauptmenü oder klicken Sie mit der rechten Maustaste auf den Projektnamen (VSClient) im Solution-Fenster und wählen Sie Add Web Reference. Klicken Sie auf der linken Seite des Fensters auf den Hyperlink Web Reference on Local Web server. Sie sehen nun alle Entdeckungs-Dateien (*.disco oder *.vsdisco), aus denen Sie VSService.vsdisco auswählen können. Unter Umständen wird die Meldung Directory Listing Denied angezeigt. Geben Sie in diesem Fall den vollständigen IISPfad der Datei VSService.vsdisco an und drücken Sie die Eingabetaste. Nun sehen Sie das in Abbildung 9.17 gezeigte Add Web Reference-Fenster. Visual Studio.NET entdeckt den Webservice und zeigt die Ergebnisse, wie in Abbildung 9.18 gezeigt, an. Klicken Sie nun auf den Hyperlink VSService.vsdisco anschließend auf den Add Reference-Button (Referenz hinzufügen). Dem Solutions-Explorer wird nun, wie in Abbildung 9.19 gezeigt, ein Bezug zu dem Webservice hinzugefügt.
Arbeiten mit ASP.NET Webdiensten 9
261
Entdeckung eines Webservice Abb. 9.18
Referenz zu einem Webservice im SolutionExplorer Abb. 9.19
6
Fügen Sie den folgenden Code hinter dem Click-Event des Buttons ein: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim s As String Dim t As New localhost.Service1() s = t.add(CInt(Param1.Text), CInt(Param2.Text)) ResultAdd.Text = s End Sub
ASP.NET 262
9
Testen der Add-Funktion Abb. 9.20
7
Erstellen Sie nun die Anwendung und drücken Sie die Taste %, um diese im Browser zu betrachten. Abbildung 9.20 zeigt das Formular. Geben Sie die Eingabewerte der beiden Textboxen Param1 und Param2 ein und klicken Sie auf den Button. Das Click-Event dieses Buttons ruft nun die Add-Methode des Webservice auf, der daraufhin das Ergebnis der Addition zurückgibt, das in der ResultAdd-Textbox angezeigt wird.
Nutzung des Webservice-Verhaltens zur Erstellung von Funktionsaufrufen Sie können Funktionen auch mit einer anderen, von Microsoft bereitgestellten Methode aufrufen, dem Web Service Behavior (Verhalten). Erinnern Sie sich daran, dass SOAP ein Protokoll und ASP.NET nur ein Werkzeug zu dessen Implementierung ist. SOAP dient hauptsächlich der Versendung und dem Empfang von XML-Paketen über HTTP. Sie brauchen jedoch kein Werkzeug, um SOAP zu starten. Dennoch ist es hilfreich, Zugang zu einer Sammlung vorgefertigter Funktionen zu haben, da Sie so das Rad nicht neu erfinden müssen. Die ASP.NET-Implementierung von SOAP erfordert die Existenz eines Proxy, worauf WebService Behavior verzichten kann. Beide Technolo-
Arbeiten mit ASP.NET Webdiensten 9 gien sind effektiv und fundiert; daher geht es bei der Präsentation von WebService Behavior lediglich um die Erweiterung Ihres Werkzeugsatzes und darum, Ihnen eine andere Art der Anwendung von SOAP zu zeigen. WebService Behavior wird mit einer einfachen HTML-Komponentendatei (HTC) als ein zusätzliches Verhaltensmuster implementiert, das im Internet Explorer 5.0 oder höher genutzt werden kann. Die Datei WebServices.HTC kann von der Microsoft-Website unter http://msdn.microsoft.com/workshop/ author/webservice/webservice.htc heruntergeladen werden. Die Datei wird im Ordner der Website gespeichert, die das Verhaltensmuster nutzt. Die Webservices können mit JavaScript auf der Clientseite aufgerufen werden. Zum Testen dieser Technik steht Ihnen ein Beispiel-Webservice zur Verfügung (behavior.asmx). Diese Datei befindet sich zusammen mit der HTC-Datei WebService.htc und zwei HTMLDateien (populate.html und add.html Unterverzeichnis behavior des Beispielordners für dieses Kapitel. Der Webservice enthält zwei einfache Funktionen. Die AddFunktion kennen Sie schon aus den vorigen Abschnitten. Die zweite Funktion Populate ist schon ein wenig interessanter. Wie bereits früher erwähnt, kann ein Webservice, der SOAP nutzt, verwendet werden, um ein ASP.NET-DataSet zu serialisieren, was hier demonstriert werden soll. Die Populate-Funktion bekommt als Eingabeparameter einen Verbindungsstring und einen SQL Select-Anfragestring. Sie gibt einen Datensatz mit Reihen aus der Datenbank nach Ausführen der SQL-Anfrage zurück, wie der folgende Auszug aus dem Code dieser Funktion zeigt: Public Function Populate(ConnStr as string, SQL as string) As DataSet Dim dv As DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet myConnection = New OleDbConnection(ConnStr) myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "vTable") Populate = ds End Function
263
ASP.NET 264
9 Hier die komplette Codeliste des Webservice: Listing 9.8: Behavior.asmx Imports System Imports System.Web.Services Imports System.Data Imports System.Data.OleDb Imports System.Text Public Class Behavior: Inherits WebService Public Function TestFunction (vInput as Boolean) As String If (vInput = TRUE) Then TestFunction = "It is the truth..." Else TestFunction = "False!False!False" End if End Function Public Function add( a as integer, b as integer) as string add = cstr(a+b) End Function Public Function Populate(ConnStr as string, SQL as string) As DataSet Dim dv As DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet myConnection = New OleDbConnection(ConnStr) myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "vTable") Populate = ds End Function End Class
Das HTML-Formular, welches die Populate-Methode des Webservice aufruft, hat den Namen Populate.html. Innerhalb dieses Formulars wird die Populate-Funktion durch Weitergabe des Verbindungsstrings und der SQL-Anfrage (Select * from Groups) als Eingabeparameter aufgerufen. Die Methode gibt alle Datensätze aus der Groups-Tabelle im XML-Format zurück.
Arbeiten mit ASP.NET Webdiensten 9
265
Sie können das XSL-Stylesheet zum Rendern der zurückgegebenen XML-Daten verwenden. Abbildung 9.21 zeigt das Ergebnis des Aufrufs der Populate-Methode.
Aufrufen der PopulateFunktion mit der Behavior-Methode Abb. 9.21
Dies ist die Codeliste von Populate.html: Listing 9.9: Populate.html var idCall = null; function init() {
WebServices.useService("behavior.asmx?WSDL","myService") ; var vcn= "Provider=SQLOLEDB; Data Source=(local);" ; vcn= vcn + "Initial Catalog=ASPNET;User ID=sa;"; var vSQL = "select * from Groups" ; idCall = WebServices.myService.callService("Populate",vcn, vSQL); } function WebServices_OnResult() { if (idCall == event.result.id) if (!event.result.error) txtResult.value = event.result.value.xml ; else txtResult.value = event.result.errorDetail.string; }
ASP.NET 266
9 Result:
WebService Behavior wird mit dem Style-Attribut an ein Element gebunden. Es erhält eine WebServices-ID, damit sich das Skript darauf beziehen kann.
Die Use-Methode des Webservice legt den Webservice-URL auf den Namen myservice. Dieser Name kann im Code wie im folgenden Beispiel als Bezug auf den Webservice verwendet werden: WebServices.useService("SQLService.asmx?SDL","myService") .
Die Populate-Funktion des SQLService.asmx erwartet zwei Parameter: den Verbindungsstring und den SQL-String. Diese werden in den Variablen vcn und vSQL gespeichert und folgendermaßen an die Funktion weitergegeben: idCall = WebServices.myService.callService("Populate",vcn, vSQL)
Die callService-Methode initiiert eine asynchrone Kommunikation zwischen WebService Behavior und dem Webservice. Das OnResult-Event wird bei Erhalt der Ergebnisse als XML-Datenpaket des Aufrufs ausgelöst. Der folgende Code enthält dann das Ergebnis und zeigt es im Textfeld auf Ihrem Bildschirm an: Function WebServices_OnResult() { If (idCall == event.result.id) If (!event.result.error) txtResult.value = event.result.value.xml ;
Arbeiten mit ASP.NET Webdiensten 9 Else txtResult.value = event.result.errorDetail.string; }
Die Add()-Methode des Webservice akzeptiert zwei ganze Zahlen als Eingabeparameter und gibt die Addition der Werte wie unten aufgeführt als Ergebnis zurück: Public Function add( a as integer, b as integer) as string add = cstr(a+b) End Function
Die Datei AddFunction.html zeigt Ihnen, wie die Add()-Funktion des Webservice aufgerufen werden kann. Hier die Codeliste der Datei: Listing 9.10: AddFunction.html var idCall = null; function init() { WebServices.useService("behavior.asmx?WSDL","myService") ; var vcn= "Provider=SQLOLEDB; Data Source=(local);" ; vcn= vcn + "Initial Catalog=ASPNET;User ID=sa;"; var vSQL = "select * from Groups" ; idCall = WebServices.myService.callService("add",5,16); } function WebServices_OnResult() { if (idCall == event.result.id) if (!event.result.error) txtResult.value = event.result.value ; else txtResult.value = event.result.errorDetail.string; }
267
ASP.NET 268
9 Result:
Der Aufruf gleicht dem Aufruf der Populate-Funktion mit dem Unterschied, dass das Event-Ergebnis ein Wert und kein XMLCode ist.
Zugriff auf Datenquellen anderer Domains Ein häufiges von XML/SOAP-Benutzern moniertes Problem sind die Zugangsverweigerungen im Internet Explorer beim Versuch, den Webservice einer anderen Domain aufzurufen. Um auf einen solchen Webservice zugreifen zu können, muss die Option Access data sources across domains des Internet Explorer entweder auf Enabled oder auf Prompted gesetzt werden. Wählen Sie hierzu Tools/Internet-Optionen. Wählen Sie dann das Security-Register. Achten Sie bei den Interneteinstellungen darauf, dass die Option Access data sources across domains des Internet Explorer entweder auf Enabled oder auf Prompted gesetzt ist, wie in Abbildung 9.22 gezeigt.
Zusammenfassung Dieses Kapitel behandelte eines der wichtigsten Themen in ASP.NET-Webservices. Technologien zur Kommunikation zwischen verschiedenen Anwendungen wie etwa DCOM, RPC und MSMQ existieren zwar bereits; sie sind bisher nicht sehr erfolgreich gewesen, da der Benutzer hier immer an eine bestimmte Technik gebunden war. Webservices jedoch basieren auf SOAP und XML, die beide von allen Maschinen und Betriebssystemen verstanden werden. In diesem Kapitel haben Sie gelernt, Webservices sowohl mit einem simplen Text-Editor als auch mit Visual Studio 7 zu nutzen. Sie haben auch gelernt, Webservices mit Web Service Behaviors aufzurufen.
Arbeiten mit ASP.NET Webdiensten 9
269
Die Option Enable Access data sources across domains des Internet Explorers Abb. 9.22
ASP.NET-Anwendungen
Erstellung eines virtuellen Verzeichnisses
272
Die Global.asax-Datei
275
Global.asax und Anwendungsstatus
280
Sitzungsstatus
283
Die Konfigurationsdatei
291
Zusammenfassung
302
ASP.NET 272
10 Eine ASP.NET-Anwendung entspricht einem virtuellen Verzeichnis. Alle ASP.NET-Objekte im gleichen virtuellen Verzeichnis bilden eine ASP.NET-Anwendung. Diese Objekte können Seiten, Webservices, Konfigurationsdateien, globale Anwendungsdateien, Zusammensetzungen und Anwendungs-Services wie etwa Sicherheitsprogramme sein.
Erstellung eines virtuellen Verzeichnisses Um ein neues virtuelles Verzeichnis in IIS unter Windows NT zu erstellen, starten Sie den Internet Service Manager. Klicken Sie mit der rechten Maustaste auf ein bestehendes Verzeichnis und wählen Sie Neu/Virtuelles Verzeichnis (New/Virtual Directory – siehe Abbildung 10.1):
Ein neues virtuelles Verzeichnis in IIS Abb. 10.1
ASP.NET-Anwendungen 10 Umwandeln eines bestehenden Ordners in ein virtuelles Verzeichnis Sie können einem bestehenden Verzeichnis den Status eines virtuellen Verzeichnisses verleihen: 1
Klicken Sie mit der rechten Maustaste auf den Ordner, den Sie in ein virtuelles Verzeichnis umwandeln wollen.
2
Wählen Sie Anwendungs-Einstellungen (Application Settings) und klicken Sie auf den Button Erstellen (Create) direkt neben der deaktivierten Default Application-Textbox (siehe Abbildung 10.2).
3
Der Button Erstellen (Create) trägt nun die Bezeichnung Entfernen (Remove) und das Namensfeld (Name) ist aktiviert. Das virtuelle Verzeichnis ist bereit (siehe Abbildung 10.3).
Erstellen eines neuen virtuellen Verzeichnisses in einem persönlichen Webserver unter Windows 2000 Um ein virtuelles Verzeichnis in einem persönlichen Webserver unter Windows 2000 zu erstellen, führen Sie die folgenden Schritte aus: 1
Starten Sie den Personal Web Server und klicken Sie auf den Button Erweitert (Advanced / siehe Abbildung 10.4).
2
Klicken Sie auf Hinzufügen (Add), browsen Sie zum Ordner und geben Sie ihm einen Namen in der AliasBox.
273
ASP.NET 274
10
Der Create-Button Abb. 10.2
Der Remove-Button Abb. 10.3
ASP.NET-Anwendungen 10
275
Neues virtuelles Verzeichnis im Personal Web Server Abb. 10.4
Die Global.asax-Datei Die Global.asax-Datei ähnelt dem Konzept der Global.asa-Datei. Ebenso wie die Global.asa-Datei verarbeitet auch die Global.asax-Datei Anwendungslogik durch Anwendungs-Events, wie etwa Application Start, Application End, Session Start und Session End, um nur einige zu nennen. Diese Datei wird dynamisch gegliedert und in eine .NET-Framework-Klasse kompiliert, sobald irgendeine Ressource innerhalb des Namensfeldes der Anwendung zum ersten Mal angefordert wird. Die Datei kann nicht direkt von Benutzern angefordert werden, da ihr Inhalt geschützt ist. Die Global.asax- und die Global.asa-Datei können nebeneinander im gleichen virtuellen Verzeichnis existieren. Hierdurch wird den ASP-Entwicklern eine Rückwärtskompatibilität geboten – ASP-Anwendungen benutzen weiterhin die Global.asa-Dateien, während die Global.asax von den ASP.NET-Anwendungen genutzt werden. Die beiden Dateien können sich jedoch keine Anwendungen oder Variablen des Sitzungsstatus (Session state variables) teilen. Wenn Sie die Global.asax-Datei verändern, erkennt das ASP.NET-Framework diese Veränderungen, komplettiert alle offenen Anfragen, startet das Application OnEvent-Event und bootet die Anwendung erneut, wodurch alle bestehenden Sta-
ASP.NET 276
10 tusinformationen gelöscht werden. Dieser Prozess ist für den Benutzer unsichtbar, der hierdurch keine Verzögerungen in der Übertragung erfährt. Die Global.asax-Datei hat zwei Events, die bei jeder Anfrage oder Rücksendung eines Formulars gestartet werden. Hierbei handelt es sich um die Events Application BeginRequest und Application EndRequest. Code, der am Anfang der Seite ausgeführt werden muss, kann in der Anwendung Application BeginRequest platziert werden. Abbildung 10.5 zeigt eine Beispielanwendung, welche die Global.asax-Datei verwendet. Der Code befindet sich im Beispielordner (Samples) im Unterverzeichnis Events. Achten Sie darauf, die Global.asax-Datei im Ursprungsverzeichnis (Root directory) des virtuellen Verzeichnisses zu platzieren. Listing 10.1: Global.asax Sub Application_Start(Sender As Object, E As EventArgs) 'Code zum Start der Anwendung hier einfügen End Sub Sub Application_End(Sender As Object, E As EventArgs) 'Anwendungsressourcen bereinigen End Sub Sub Session_Start(Sender As Object, E As EventArgs) Response.Write("Session Start Fired...") End Sub Sub Session_End(Sender As Object, E As EventArgs) 'Sitzungsressourcen bereinigen End Sub Sub Application_BeginRequest(Sender As Object, E As EventArgs) Response.Write(" Global.asax Demo") Response.Write("Begin Request Fired...")
ASP.NET-Anwendungen 10 End Sub Sub Application_EndRequest(Sender As Object, E As EventArgs) Response.Write("End Request Fired...") End Sub
Die folgende Website ruft diese Datei auf: Listing 10.2: GlobalTest.aspx Sub Page_Load(Sender As Object, E As EventArgs) Response.Write("Page.Load Fired...") End Sub Sub Session_End(Sender As Object, E As EventArgs) Session.Abandon() Response.Redirect("GlobalTest.aspx") End Sub
Wenn die Seite angefordert wird, tritt die folgende Event-Sequenz auf: 1
Application BeginRequest
2
Session Start
3
Page Load
4
Application EndRequest
277
ASP.NET 278
10 Abbildung 10.5 zeigt das Ergebnis:
Die erste Anfrage Abb. 10.5
Wenn die Seite erneuert wird, tritt die folgende Event-Sequenz auf: 1
Application_BeginRequest
2
Page Load
3
Application_EndRequest
Abbildung 10.6 zeigt das Ergebnis: Wenn die Internetsitzung beendet wird, tritt die folgende Event-Sequenz auf: 1
Application_BeginRequest
2
Session_Start
3
Page Load
4
Application_EndRequest
Bei Abbruch einer Sitzung wird eine neue Internetsitzung generiert und das Session_Start-Event wird gestartet, wie die Abbildung 10.7 zeigt:
ASP.NET-Anwendungen 10
279
Erneuerung der Seite Abb. 10.6
Abbruch der Sitzung Abb. 10.7
Die Methoden Locking und Unlocking dienen als Sicherung gegen Datenkorruption, wenn mehrere Benutzer gleichzeitig versuchen, dieselbe Variable zu aktualisieren. Die Syntax ist geradlinig, wie das folgende Beispiel zeigt:
ASP.NET 280
10
Wenn die Ausführung einer Seite beendet ist, die Übertragung zu lange dauert oder ein unbearbeiteter Fehler auftritt, wird das Anwendungs-Objekt automatisch entriegelt. Bei der Speicherung von Daten in Anwendungs-Objekten ist Vorsicht geboten, da wichtige Ressourcen, die besser an einem anderen Ort gespeichert werden sollten, absorbiert werden können. Das Anwendungsobjekt kann in einer Webfarm oder einem Webgarden nicht aufrecht erhalten werden, was eine Einschränkung dieser Technologie bedeutet.
Anwendungen oder sitzungsbezogene Objekte Sie können .NET-Framework-Klassen oder klassische COMKomponenten entweder mit einer Anwendungsinstanz (Appinstance), einer Sitzung oder einem Anwendungsbereich definieren und hierbei die Objektmarkierungen nutzen. Die Anwendungsinstanz impliziert, dass das Objekt spezifisch für eine HTTP-Anwendung ist und nicht gemeinsam genutzt wird.
Global.asax und Anwendungsstatus Sie können anwendungsbezogene Variablen in der Global.asax-Datei speichern. Das folgende Beispiel zeigt Ihnen, wie Sie eine Datenansicht (DataView) in einer Anwendungsvariablen speichern können, die dann von der gesamten Anwendung genutzt werden kann. Der Code für dieses Beispiel befindet sich im Unterverzeichnis ...samples\Application auf der CD des Buches.
ASP.NET-Anwendungen 10 Programmieren Sie zunächst das Application_Start-Event wie hier beschrieben: Listing 10.3: Global.asax Sub Application_Start(Sender As Object, E As EventArgs) 'Code zum Start der Anwendung hier einfügen Dim dv As DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim ConnStr As String Dim SQL As String ConnStr = "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;" myConnection = New OleDbConnection(ConnStr) SQL = "select * from groups " myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "groups") dv = new DataView(ds.Tables("groups")) Application("Source") = dv End Sub
Ich habe Datensätze aus der Groups-Tabelle extrahiert und eine Datenansicht (DataView) mit den Ergebnissen gefüllt. Dann habe ich diese Datenansicht in einer Anwendungsvariablen namens Source gespeichert. Da ich mit der Datenbank interagieren muss, war es notwendig, die Namensfelder (Namespaces) System.Data und System.ADO zu importieren. Ich kann nun die Source-Anwendungsvariable benutzen, um das DataGrid in einem Webformular zu füllen:
281
ASP.NET 282
10 Listing 10.4: ApplicationState.aspx Sub Page_Load(Src As Object, E As EventArgs) Dim Source As DataView = Application("Source") vSpan.InnerHtml = Source.Table.TableName vGrid.DataSource = Source vGrid.DataBind() End Sub DataSource in Application_OnStart Table:
Abbildung 10.8 zeigt das Ergebnis:
ASP.NET-Anwendungen 10
283
Der Anwendungs-Status Abb. 10.8
Sitzungsstatus Im Gegensatz zum Anwendungs-Objekt, das sich nur wenig von den früheren ASP-Versionen unterscheidet, hat das Session-Objekt (Sitzungs-Objekt) einige größere Veränderungen erfahren. Eine Sitzungsvariable ist ein Schlüsselwertpaar, das für die Dauer einer Internetsitzung eines Benutzers festgelegt und gelesen werden kann. Sie können beispielsweise auf folgende Weise einen Wert in einer Sitzungsvariablen (Session variable) speichern: Session("Name") = "Hersh Bhasin"
Auf diese Sitzungsvariable kann auf einer Website folgendermaßen zugegriffen werden: Dim ls_name As string ls_name = Session("Name")
Jeder Sitzung wird ein individueller Schlüssel zugewiesen, der in einem HTTP-Cookie gespeichert und bei jeder Anfrage an den Server gesendet wird. Der Server liest den Schlüssel und stellt den Serverstatus wieder her. Das Problem dieser Implementierung besteht darin, dass Benutzer diese Cookies aus Angst um ihre Privatsphäre möglicherweise ablehnen, wo-
ASP.NET 284
10 durch ein Statusmanagement unmöglich gemacht wird. Ein weiteres Problem besteht darin, dass dieses Statusmanagement mit dem Webserver verbunden ist, so dass bei einer Erneuerung oder einer Fehlfunktion des Servers die Statusinformationen verloren gehen. Der dritte Nachteil besteht darin, dass jeder ASP-Server seinen eigenen Status aufrecht erhält und der Benutzerstatus verloren geht, wenn der Benutzer nicht wieder zu demselben Server zurückkehrt. Dies ist immer dort der Fall, wo ISPs Proxy load balancing-Lösungen einsetzen. ASP.NET löst dieses Problem. Der ASP.NET-Sitzungsstatus ist prozessunabhängig. Das bedeutet, dass selbst bei einem Ausfall oder Neustart des Servers der Status aufrecht erhalten wird. Der Sitzungsinhalt kann in der Datenbank eines SQL-Servers fortgesetzt und die Statusinformationen nach einem Server-Fehler wieder neu geladen werden. Die Benutzung von Sitzungsvariablen war häufig ein Problem in Webfarmen, wo mehrere Server Benutzeranfragen bearbeiten. Dieses Problem gibt es nicht mehr. Durch das prozessunabhängige Modell ermöglicht es ASP.NET den Benutzern in einer Webfarm den Prozess des Sitzungsstatus gemeinsam zu nutzen. Die Konfiguration des Sitzungsstatus wird von den Einstellungen in der web.config-Datei vorgenommen. Diese Datei wird später im Kapitel noch sehr ausführlich erläutert. Die voreingestellte Datei (machine.config) befindet sich im Verzeichnis ...\Frameworkl\[Version]\ des Windows-Ordners (z.B. WinNT\Framework\[Version]). Jede Anwendung kann über eine eigene web.config-Datei verfügen, wobei die darin enthaltenen Einstellungen die machine.config-Datei überschreiben. Die folgenden sechs Einstellungen können zur Konfiguration des Sitzungsstatus () vorgenommen werden: Mode: Inproc, SQLServer, und StateServer. InProc steht
für In-Process und ist die traditionelle Einstellung im ASP-Statusmanagement. Es gibt zwei Arten von prozessunabhängigen Modi: speicherbasiert (State-Server) und SQL-Server-basiert (SQL-Server). Diese Arten werden später noch genauer erklärt. Cookieless: Entweder wahr oder falsch (true oder false).
Die Grundeinstellung ist false. True impliziert, dass der Modus aktiviert ist.
ASP.NET-Anwendungen 10 Timeout: Dauer, für die eine Sitzung als gültig angese-
hen wird. SqlConnectionString: Wird im SQL-Server-Modus verwendet. Spezifiziert den Verbindungs-String zur Datenbank für das ASP.NET-Statusmanagement. StateConnectionString: Wird im State-Servermodus verwendet. Identifiziert die TCP/IP-Adresse und den Port des Windows NT-Service, der das Statusmanagement liefert.
Ich habe ein Test-Webformular erstellt, das ich für meine Erläuterungen verwende. Das Formular hat zwei einfache Funktionen zur Bestimmung der Sitzungsvariablen und zum Wiederfinden des Wertes dieser Variablen, wie Sie in der folgenden Codeliste erkennen können: Listing 10.5: Session.aspx Sub AddSession(sender As Object, e As EventArgs) Session("MySession") = text1.Value message.text = "Session Variable stored as : " +Session("MySession").ToString() End Sub Sub CheckVariable(sender As Object, e As EventArgs) If Session("MySession") = "" Then message.text = "Session Data has been erased" Else message.text = "Stored Session Variable is : " + Session("MySession").ToString() End If End Sub Session State
285
ASP.NET 286
10
Prozessabhängiger Modus Dies ist der traditionelle Modus, in dem ASP das Statusmanagement gehandhabt hat. Der Status wird innerhalb des Prozesses verwaltet und geht verloren, sobald der Prozess erneuert wird. Vielleicht fragen Sie nach dem Sinn dieses Modus, da es doch auch den viel effizienteren, prozessunabhängigen Modus gibt. Der Grund hierfür ist die Leistung. Prozessunabhängige Modi verursachen zusätzliche Kosten, da die Daten ständig zwischen Prozessen oder SQL-Servern hin- und hertransportiert werden müssen. Zur Erstellung eines Statusmanagements in diesem Modus führen Sie die folgenden Schritte durch: 1
Setzen Sie das mode-Attribut des sessionState in der web.config-Datei auf InProc (die Grundeinstellung). Wenn diese Einstellung gewählt wurde, sind Timeout und Cookieless die einzigen anderen web.config-Einstellungen.
2
Starten Sie Session.aspx. Speichern Sie einen Wert des Sitzungsstatus. Stoppen Sie die IIS und starten Sie diese erneut mit Hilfe der Befehlszeilen-Utility iisreset, die in IIS5 enthalten ist: C:\iisreset [computername] /RESTART (or simply issue the command iisreset from the command prompt)
ASP.NET-Anwendungen 10 3
Klicken Sie auf den Button Check Session Variable (Sitzungsvariable überprüfen). Sie werden sehen, dass die Sitzungsvariable verloren ging.
Der Code für dieses Beispiel befindet sich im Unterverzeichnis ...\samples\sessionstate\inprocess für dieses Kapitel auf der CD dieses Buches.
Prozessunabhängiger Modus Der prozessunabhängige Modus hält den Status in einem separaten Prozess aufrecht. Dieser Modus vereint die Verlässlichkeit eines separaten Prozesses mit dem Leistungsvorteil des Lesens und Schreibens aus einem Speicher. Dieser Modus sollte verwendet werden, wenn Leistung ein wichtiger Punkt ist, Sie aber nicht voraussagen können, welchen Server ein Benutzer zur Anforderung einer Anwendung verwendet. Beachten Sie die folgenden Schritte zur Einstellung dieses Modus: 1
Setzen Sie das Mode-Attribut von SessionState in der web.config-Datei auf StateServer. Hierdurch wird ASP.NET angewiesen, auf dem Server nach dem ASP.NET-Status-Service und in der web.config-Datei, die in diesem Fall der lokale Server ist, nach den spezifizierten Server- und Port-Einstellungen zu suchen.
2
Das ASP.NET SDK beinhaltet einen Windows NT Service namens aspnet_state, der von ASP.NET zur prozessunabhängigen Statusverwaltung genutzt wird.
287
ASP.NET 288
10 Starten Sie diesen Service durch Eingabe der folgenden Befehlszeile: net start aspnet_state
Abbildung 10.9 zeigt das Ergebnis.
Starten des ASPStateService Abb. 10.9
3
Starten Sie Session.aspx. Speichern Sie einen Wert des Sitzungsstatus. Stoppen Sie die IIS und starten Sie diese erneut mit Hilfe der Befehlszeilen-Utility iisreset, die in IIS5 enthalten ist:
4
Klicken Sie auf den Button Check Session Variable. Sie werden sehen, dass der Status beibehalten wurde.
Der Code für dieses Beispiel befindet sich im Unterverzeichnis ...\samples\sessionstate\Outofprocess für dieses Kapitel auf der CD dieses Buches.
SQL-Server-Modus Dieser Modus sollte gewählt werden, wenn Zuverlässigkeit Ihr oberstes Gebot ist. Die Datenbank kann gruppiert werden, um Server-Fehler zu bearbeiten. Der Nachteil dieses Modus ist die schlechtere Leistung. Dieser Modus kann folgendermaßen eingestellt werden:
ASP.NET-Anwendungen 10 1
Erstellen Sie die ASPState-Datenbank mit Hilfe der in ASP.NET im Ordner ...\Microsoft.NET\Framework\[Version]\ enthaltenen Datei InstallSqlState.sql und der osql.exe-utility: osql -S [server name] -U [user] -P [password] < InstallSqlState.sql
Sie können die Datei auch direkt im SQL Query Analyzer-Tool des Microsoft SQL-Servers öffnen und ausführen. (Diese Methode ist vorteilhaft, weil Sie sich keine Passwörter, Server-Namen oder Ähnliches merken müssen. Sie können sich mit der SystemadministratorBerechtigung einloggen und das Skript ausführen.) Dieser Prozess erstellt eine Datenbank namens ASPState, die 16 gespeicherte Prozeduren und 2 Tabellen in temp.db enthält. Die Tabellen sind AspStateTempSessions und AspStateTempApplications. Starten Sie den SQL-Server neu, da einige Startprozeduren erstellt wurden, die nun ausgeführt werden müssen. Ein Skript zur Deinstallation der ASPState-Datenbank ist ebenfalls enthalten. Das Skript hat den Namen UninstallSqlState.sql. 2
Setzen Sie das Mode-Attribut von SessionState in der Konfigurations-Datei auf SQL-Server.
289
ASP.NET 290
10 3
Starten Sie Session.aspx. Speichern Sie einen Wert des Sitzungsstatus. Stoppen Sie die IIS und starten Sie diese erneut mit Hilfe der Befehlszeilen-Utility iisreset, die in IIS5 enthalten ist:
4
Klicken Sie auf den Button Check Session Variable. Sie werden sehen, dass der Status beibehalten wurde.
5
Stellen Sie eine Verbindung mit der Tempdb-Datenbank her und führen Sie die folgenden Anfragen aus:
Beibehaltener Status im SQL-Server Abb. 10.10
select * from AspStateTempSessions select * from AspStateTempApplications
Die Daten werden in den Tabellen AspStateTempSessions und AspStateTempApplications des SQL-Servers gespeichert. Eine individuelle Sitzungs-ID wird in der Datenbank gespeichert. Abbildung 10.10 zeigt das Ergebnis. Sie könnten die Zuverlässigkeit beim Speichern des Sitzungsstatus noch weiter erhöhen, wenn Sie die SQL-Server gruppieren, so dass bei einem Ausfall eines Servers ein anderer den Status repliziert und einspringt.
ASP.NET-Anwendungen 10 Cookiefreier Status Dieses Feature des ASP.NET-Sitzungsstatus ermöglicht Clients, den Cookie abzuschalten und trotzdem die Vorzüge von ASP.NET zu genießen. In diesem Modus wird die Sitzungs-ID in den URL gequetscht: http://localhost/( hmxz0d5554l1bt45faqudj55)/Application/Sessione.aspx
Um diesen Status zu aktivieren, müssen Sie lediglich das Cookieless-Attribut auf True setzen:
Cookiefreie Sitzungen werden von allen Modi unterstützt.
Die Konfigurationsdatei ASP.NET kann mit Hilfe zweier Konfigurationsdateien (machine.config und web.config) konfiguriert werden. Jeder Rechner verfügt über eine einzelne machine.config-Datei, in der Informationen zur Grundkonfiguration enthalten sind. Jede Anwendungsdatei kann optional eine eigene web.config-Datei haben. Wenn eine solche Datei existiert, so übersteuert sie die machine.config-Datei im Ordner WinNt\Framework\[Version]\. Die lokale web.config-.Datei kann nur die Bereiche beinhalten, die sie benötigt. Diese Bereiche übersteuern dann die voreingestellte machine.config-Datei. Die nicht in der lokalen web.config-Datei spezifizierten Bereiche werden dann von der machine.config-Datei gelesen. Diese Konfigurationsdateien sind XML-basierte Textdateien. Hier ein Beispiel für eine Konfigurationsdatei mit zwei Einstellungen:
291
ASP.NET 292
10
Diese Dateien sind nicht direkt über einen Browser zugänglich. Das vorige Beispiel zeigt eine Datei mit zwei Konfigurationsbereichen: und . Da der sessionStateBereich bereits beschrieben wurde, folgen hier nun Erläuterungen zu einigen anderen Bereichen dieser Datei.
Dies ist das Ur-Element. Alle Konfigurationsbereiche müssen zwischen den Configuration-Markierungen enthalten sein.
Der appSettings-Bereich ermöglicht es Ihnen, die web.configDatei als ini-Datei zu benutzen. Sie können hierin anwendungsweite Einstellungen speichern, wie etwa den DSN-Namen, der dann für alle Seiten in der Anwendung verfügbar ist: Nehmen Sie in der web.config-Datei folgende Einstellung vor:
Add ist das einzige Subelement dieses Bereiches. Das Subelement Add unterstützt zwei Attribute: Key und Value. Key ist der Name der Variablen, die im Value-Attribut gespeichert wird. Sie können so viele Add-Subelemente haben, wie Sie benötigen.
ASP.NET-Anwendungen 10 Auf einer Website werden Sie den gespeicherten Wert mit Visual Basic.NET folgendermaßen extrahieren: Listing 10.6: AppSettingVb.aspx Sub Page_Load(Src As Object, E As EventArgs) Dim dsn As String = ConfigurationSettings.AppSettings("dsn") response.write(dsn) End Sub
Dieser Code befindet sich im Ordner ...\samples\configFile\configAppSettings auf der CD des Buches. Hier ist der gleiche Code in C#: Listing 10.7: AppSettingC.aspx public void Page_Load(Object sender, EventArgs e) { String s = ConfigurationSettings.AppSettings["dsn"]; dsn.InnerHtml =s; }
Der DSN-Eintrag in der web-config-Datei ist:
Diese Sektion hat ein Attribut (debug) und zwei Unterbereiche ( und ). Das Debug-Attribut ist eine Boolean-Einstellung, die auf True oder False gesetzt werden kann. Bei True wird die Datei im Debug-Modus (Fehlersuche) kompiliert. Das dauert ein wenig länger und daher sollte diese Option in Produktionsseiten (production sites) abgeschaltet werden.
293
ASP.NET 294
10 Im Compiler-Bereich stellen Sie die Sprache und die Erweiterungsattribute für jede Sprache, die Sie verwenden, ein. Wenn Sie beispielsweise die Sprache Visuals Basic mit der Erweiterung vb versehen, werden alle Webformulare mit dieser Erweiterung Visual Basic als voreingestellte Skriptsprache haben, so dass Sie diese nicht noch einmal in der Seitendirektive oder den Skriptmarkierungen definieren müssen. Der Unterbereich ermöglicht Ihnen zu spezifizieren, welche Zusammensetzungen (Assemblies) Sie in die Kompilierung einer Ressource integrieren möchten. Sie werden sich erinnern, dass Sie bei der Erstellung von Business-Objekten (Kapitel 8) und benutzerdefinierten Kontrollen (Kapitel 7) verschiedene Zusammensetzungen in die bat-Dateien, welche die von Ihnen erstellten Objekte kompilieren, integrieren mussten. Hier können wir die Zusammensetzungen spezifizieren. Sie müssen diese Zusammensetzungen aber immer noch mit Hilfe der Seitendirektive in die Website importieren. Das -Unterelement hat drei Subelemente: Add, Remove und Clear. Das Add-Unterelement fügt Zusammensetzungen hinzu. Sie können * benutzen, um alle Zusammensetzungen im bin-Verzeichnis hinzuzufügen. Remove entfernt den Bezug zu einer Zusammensetzung und Clear entfernt alle Bezüge, die in der Datei config.web enthalten oder aus ihr entstanden sind.
ASP.NET-Anwendungen 10 ASP.NET bietet eine ganze Reihe nützlicher Informationen bei Auftreten eines Fehlers. Dies mag für Entwickler ganz nützlich sein, wird die Benutzer der Site aber möglicherweise erschrecken. Die Custom Error-Einstellung ermöglicht es Ihnen, eine benutzerdefinierte Fehlerseite zu erstellen, die benutzerdefinierte Fehlermeldungen anzeigt. Sie können auch bestimmte Fehler (z.B. Fehlercode 404 oder 500) zu separaten, benutzerdefinierten Fehlerseiten zurückleiten. Der benutzerdefinierte Fehlerbereich hat zwei Attribute: defaultRedirect: Spezifizieren Sie hier Ihre benutzerdefinierte Fehlerseite. Mode: On, Off oder RemoteOnly. Der On-Modus leitet alle Fehler zur benutzerdefinierten Fehlerseite zurück. Der Off-Modus schaltet benutzerdefinierte Fehler aus und der RemoteOnly-Modus schaltet die benutzerdefinierten Fehler nur für Fernabfragen ein – wenn Sie sich auf dem gleichen Server befinden und einen Fehler verursachen, sehen Sie dessen komplette Details, während die Benutzer der Site nur die benutzerdefinierte Fehlermeldung sehen. Diese Option kann recht hilfreich sein, da der Entwickler mit allen Fehlerinformationen versorgt wird. Sie werden nun lernen, eine benutzerdefinierte Fehlerseite zu erstellen (custom error page). Der Code zu diesem Beispiel befindet sich im Unterverzeichnis ...\samples\configFile\ CustomErrors auf der CD des Buches. Listing 10.8: web.config für benutzerdefinierte Fehler
295
ASP.NET 296
10 Ich habe die Datei als HandleError.aspx spezifiziert: Listing 10.9: HandleError.aspx body {font-family:Tahoma,Arial,sans-serif; fontsize:10pt} Default Custom Error Page There was an error in the page Return to the previous page
Ich habe eine Testseite erstellt und versuche zu einer nicht in dieser Datei existierenden Seite zu navigieren und verursache so einen Fehler. Hier ist die Codeliste dazu: Listing 10.10: ErrorTest.aspx Sub cause_error(Source As Object, E As EventArgs) Response.Redirect("NonExistantPage.aspx") End Sub
Der Bereich unterstützt einen Unterbereich: Das Subelement . Sie können Subelemente verwenden, um bestimmte Fehlernummern zu Ihrer eigenen benutzerdefinierten Fehlerseite zurückzuleiten, wie das folgende Beispiel zeigt:
ASP.NET-Anwendungen 10
Alle Fehler mit dem dem Status-Code 500 werden hierdurch zu der benutzerdefinierten Fehlerseite 500Error.aspx zurückgeleitet.
Der Unterbereich Globalization hat die folgenden fünf Attribute: requestEncoding: Dies ist die voreingestellte Codierung für alle Anfragen. responseEncoding: Dies ist die voreingestellte Codierung für alle Antworten. FileEncoding: Dies ist die voreingestellte Codierung für alle aspx-, asmx- und aspc-Dateien. Culture: Dies ist die voreingestellte Kultur für die Bearbeitung von Anfragen. Sie enthält Informationen bezüglich Sprache, Kalender und Schreibsystem wie beispielsweise English = en. Informationen hierzu finden Sie im Namensfeld System.Globalization in der CultureInfo-Klasse. uiCulture: Dies ist die voreingestellte Kultur für das Suchen nach Ressourcen, die einen CultureInfo-Wert wie etwa en erwartet. Hier ein Beispiel:
297
ASP.NET 298
10 httpHandlers ermöglicht es Ihnen, eingehende Anfragen an NET Framework-Klassen weiterzuleiten, die diese Anfragen bearbeiten können. Zum Beispiel:
Diese Einstellung sagt dem Framework, dass alle Anfragen an die Datei mit der Erweiterung aspx von der NET FrameworkKlasse System.Web.UI.PageHandlerFactory bearbeitet werden sollen. Dieser Bereich hat die folgenden drei Attribute: verb: Dieses Attribut befiehlt dem .NET-Runtimemodul die Anfrage mit GET, POST oder PUT zu verarbeiten oder alle drei zu verwenden und durch ein Komma zu trennen (ein * impliziert das Gleiche). path: Der Pfad zu einer Datei (oder zu mehreren Dateien mit der gleichen Erweiterung), die verarbeitet werden soll. type: Der Name der Zusammensetzung oder Klasse, welche die Anfrage bearbeitet. Sie können Ihre eigenen httpHandler programmieren. Wenn Sie zum Beispiel den Zugang zu Webformularen mit der Bezeichnung default.aspx verweigern wollen, müssen Sie eine einfache Klasse programmieren und Anfragen an dieses Formular folgendermaßen bearbeiten: Listing 10.11: handler.vb Imports System.Web Namespace Hersh Public Class CustomHandlerVB : Implements IHttpHandler Public Sub ProcessRequest(Context As HttpContext) Implements IHttpHandler.ProcessRequest Context.Response.Write("Sorry! Access to
ASP.NET-Anwendungen 10 this resource is Denied...") End Sub Public ReadOnly Property IsReusable As Boolean Implements IHttpHandler.IsReusable Get Return true End Get End Property End Class End Namespace
Mit dem IhttpHandler-Interface können Sie einen benutzerdefinierten HTTP Handler erstellen. Dieses Interface enthält nur zwei Methoden: IsResuable und ProcessRequest. Die IsReusableMethode sagt der Http-Factory, ob dieselbe Instanz zur Bearbeitung mehrerer Anfragen benutzt werden kann. ProcessRequest nimmt die HttpContext-Instanz als Parameter, wodurch die Antwort und die Anfrage inhärent zugänglich sind. Im vorigen Beispiel wurden die Anfragedaten ignoriert und ein String als Antwort zurückgegeben. Kompilieren Sie die Klasse jetzt folgendermaßen: Listing 10.12: make.bat set outdir=g:\aspNetSamples\bin\CustomHandlerVB.dll set assemblies=System.Web.dll vbc /t:library /out:%outdir% /r:%assemblies% handler.vb pause
Vergewissern Sie sich, dass der outdir-Parameter auf Ihr lokales bin-Verzeichnis gerichtet ist. Fügen Sie der web.config-Datei den folgenden Abschnitt hinzu:
299
ASP.NET 300
10 Versuchen Sie nun, eine Datei namens default.aspx mit IIS zu öffnen. Sie werden eine Meldung erhalten, dass Ihnen der Zugriff auf diese Ressource verweigert wurde. Den kompletten Code für dieses Beispiel finden Sie im Unterverzeichnis ...\samples\configfile\httpHandler auf der CD des Buches.
Dieser Bereich ermöglicht Ihnen die Konfiguration von HTTPModulen für Ihre Anwendungen. Die meisten Klassen und Zusammensetzungen, die Sie benötigen, sind bereits in der Grundeinstellung enthalten. Sie können diesen Bereich verwenden, um Ihre eigenen Handler hinzuzufügen.
In diesem Bereich können Sie das Prozessmodell Ihrer Webanwendung konfigurieren. Hierzu stehen Ihnen eine Reihe von Einstellungsmöglichkeiten zur Verfügung:
Die Attribute haben folgende Bedeutung: enable: Diese Boolean-Variable bestimmt, ob das Element aktiviert ist. Timeout: Die Dauer, nach der ein neuer IIS-Arbeitsprozess gestartet wird, um den alten Prozess zu ersetzen. IdleTimeout: Die maximale Leerlaufzeit eines Arbeitsprozesses. ShutDownTimeout: Die Dauer, nach der ein Arbeitsprozess automatisch beendet wird. RequestLimit: Die Anzahl der Anfragen, die ein Arbeitsprozess bearbeiten kann, bevor er beendet und durch einen neuen Prozess ersetzt wird. RequestQueueLimit: Die Anzahl der Anfragen, die sich ansammeln können, bevor ein Prozess wegen Überlastung beendet und durch einen neuen Prozess ersetzt wird. CpuMask: Kontrolliert die Anzahl der Arbeitsprozesse in Webgärten. WebGarden: Kontrolliert die Prozessoraffinität in Webgärten. Vielleicht möchten Sie auch mit den Einstellungen des memoryLimit experimentieren, um zu versuchen, die Leistung der Seite zu erhöhen. Ein Server mit geringem Speicher würde die Voreinstellung von MemoryLimit (40%) sehr schnell erreichen, wodurch der Prozess in einer Schleife neu gestartet und wieder beendet würde.
301
ASP.NET 302
10
Zusammenfassung In diesem Kapitel haben Sie einiges über die Konzepte von Anwendungen in ASP.NET erfahren. Sie haben auch die Global.asax und ihre Bedeutung für die Beibehaltung von Variablen im Anwendungsbereich kennen gelernt. Die Beibehaltung von Variablen in einem Sitzungsstatus hat, wie Sie gelernt haben, in ASP.NET eine radikale Änderung erfahren. Zu guter Letzt wurden in diesem Kapitel noch die web.config und ihre Unterbereiche erklärt. Zwei wichtige Unterbereiche der web.Config-Datei werden in separaten Kapiteln noch ausführlich erläutert. Es handelt sich hierbei um die Themen Tracing (Kapitel 12) und Sicherheit (Kapitel 13).
Caching
Output Caching
305
Page Data Caching
307
Datei- und Schlüsselabhängigkeit
308
Zusammenfassung
312
ASP.NET 304
11 Caching ist eine wichtige Technik beim Aufbau schneller und effizienter Websites. Die Theorie, die hinter Caching steht, besagt, dass einige Objekte der Website bei der Erstellung hohe Kosten verursachen. Solche Objekte sollten daher nur einmal erstellt und dann für einen festen Zeitraum oder bis zu ihrer Modifizierung gespeichert werden. Aufrufe erzeugen in diesem Fall dann keine neue Ressource, sondern finden diese im Cachespeicher. Im Allgemeinen handelt es sich hierbei um Ressourcen, die für einen längeren Zeitraum unverändert bleiben – beispielsweise Einkaufslisten und Preislisten. Das ASP.NETFramework selbst benutzt diese Cachespeicher. Wenn der erste Aufruf an eine ASP-Seite erfolgt, wird diese als Instanz der Seiten-Klasse kompiliert und im Cache des Servers gespeichert. Nachfolgende Anfragen an die Seite laden diese dann aus dem Cachespeicher, bis die Seite modifiziert wird oder die Speicherperiode ausläuft. Zu diesem Zeitpunkt wird der Cachespeicher aktualisiert. ASP.NET unterstützt zwei Cachearten: Output Caching Data Caching Output Caching bezeichnet das Speichern einer kompletten Seite. Diese Technik ist sehr nützlich bei stark frequentierten Seiten. Damit das Caching funktioniert, muss die von verschiedenen Benutzern angeforderte Seite in allen Aspekten identisch sein. Wenn die Anfragen nicht an identische Seiten gehen, können diese nicht aus dem Cachespeicher gezogen werden, sondern müssen bei jeder Anfrage neu generiert werden. Aus diesem Grund arbeitet das Output Caching mit GETAnfragen (und Anfragestrings), jedoch nicht mit POST. Bei einer GET-Anfrage kann das ASP-Runtime-Modul den Anfragestring betrachten und allen Anfragen mit identischem String die Cacheversion der Ressource zurückgeben. Data Caching beinhaltet die Identifizierung von Objekten oder Daten auf einer Seite, deren Konstruktion hohe Kosten verursacht, um nur diese Daten und Objekte zu speichern.
Caching 11
Output Caching Output Caching wird durch eine einfache Direktive am Anfang der Seite durchgeführt:
Diese Direktive besagt, dass die Seite für 10 Sekunden in Erinnerung bleibt. Die erste Anfrage an die Seite speichert diese im Cachespeicher. Für eine Dauer von 10 Sekunden wird nun allen Benutzern mit einer Anfrage an diese Seite die Cacheversion zurückgegeben. Hierdurch wird die Leistungsfähigkeit der Seite drastisch erhöht. Hier ein Beispiel: Listing 11.1: MastersGrid.aspx Masters Table Sub Page_Load(Source As Object, E As EventArgs) Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim ConnStr As String Dim SQL As String 'Verbindungssyntax ConnStr = "Provider=SQLOLEDB; Data Source=(local);" ConnStr = ConnStr + " Initial Catalog=ASPNET; User ID=sa" myConnection = New OleDbConnection(ConnStr) 'DataSetCommand SQL = "select * from Masters" myCommand = New OleDbDataAdapter(SQL, myConnection) 'Benutze die Fill-Methode des DataSetCommand, um einen Datensatz zu füllen myCommand.Fill(ds, "Masters") 'Bindung eines Datengitters DataGrid1.DataSource=ds.Tables("Masters").DefaultView
305
ASP.NET 306
11 DataGrid1.DataBind() Anzeigen der Erstellungszeit TimeMsg.Text = DateTime.Now.ToString() End Sub Last generated on:
Das Webformular bindet ein DataGrid an ein DataSet, das aus der Datenbank heraus gefüllt wird. Alle folgenden Anfragen der nächsten 60 Sekunden werden aus dem Cachespeicher bedient. Die last generated-Zeit zeigt Ihnen an, wann die Seite erstmalig generiert wurde. Für die nächsten 60 Sekunden wird bei der Aktualisierung des Browsers die gleiche Zeit angezeigt, wodurch erkennbar ist, dass die Seite aus dem Cachespeicher zurückgegeben wird. Wenn ein Anfragestring in allen Aspekten übereinstimmt, wird die Seite aus dem Cachespeicher gerendert. In allen anderen Fällen wird die Seite wie im folgenden Beispiel neu generiert: Listing 11.2: querystring.aspx Sub Page_Load(Src As Object, E As EventArgs) Dim querystring As String querystring = Request.QueryString("name") nameMsg.text = querystring TimeMsg.Text = DateTime.Now.ToString() End Sub QueryString & the Output Cache
Hersh
Bhasin
John
Smith
Bob
name: Last generated on:
In diesem Beispiel sendet das Formular an sich selbst und gibt einen Anfragestring zurück. Am unteren Rand wird eine Zeit eingeblendet, die Ihnen anzeigt, wann die Seite erstellt wurde. Wenn Sie beim ersten Mal einen Namen auswählen, wird am unteren Rand ein Zeitstempel eingeblendet. Wenn Sie diesen nun erneut auswählen, bleibt die Zeit unverändert, woran Sie erkennen können, dass die Seite aus dem Cachespeicher erstellt wurde. Wenn Sie einen anderen Namen wählen, ändert sich die Zeit wieder (da sich auch der Anfragestring ändert).
Page Data Caching ASP.NET bietet eine robuste Cacheengine, die Sie zur Speicherung bestimmter Objekte nutzen können. Im Gegensatz zum Output Caching, bei dem immer eine ganze Seite gespeichert wurde, ermöglicht das Data Caching das Speichern bestimmter Objekte auf der Seite, deren Konstruktion teuer ist und die daher von dieser Option profitieren. Der ASP.NET-Cache ist für jede Anwendung privat. Der Speicher ist zugangssicher; er unterstützt den gleichzeitigen Zugriff durch verschiedene Benut-
307
ASP.NET 308
11 zer. Die Syntax ähnelt den Anwendungs- und SitzungsObjekten, wie der folgende Code zeigt: Werte im Cachespeicher hinzufügen: Cache("MyKey") = "SomeValue"
Alternativ kann auch der folgende Code verwendet werden: Cache.Insert("MyKey","SomeValue")
Extrahieren von Werten aus dem Cachespeicher: Dim ls_string as string Ls_string = Cache("MyKey")
Entfernen von Werten aus dem Cachespeicher: Cache.Remove("MyKey")
Datei- und Schlüsselabhängigkeit Websites speichern häufig Informationen in XML-Dateien. Einige dieser Dateien müssen lange Zeit nicht geändert werden. So werden zum Beispiel Produktkataloge oder Preislisten nur dann geändert, wenn ein neues Produkt hinzugefügt oder ein bestehendes Produkt modifiziert wird. Es wäre eine Verschwendung von Ressourcen, diese Dateien bei jeder Anfrage dynamisch neu zu generieren. Entwickler haben sich hier häufig mit so genannten Batch-Updates beholfen. (Jjedes Mal, wenn eine XML-Datei modifiziert wird, regeneriert man diese und speichert sie auf dem Server.) Anfragen werden dann an diese statische Datei weitergeleitet. Dieser Prozess erfordert jedoch eine manuelle Intervention und wirft eine Reihe von damit verbundenen Problemen auf. Das ASP.NET-Caching ermöglicht es Ihnen, einen Link oder eine Abhängigkeit zwischen der XML-Datei und der im Cachespeicher abgelegten Datei zu erstellen, so dass der Cachespeicher bei jeder Änderung der XML-Datei aktualisiert wird und alle Anfragen an diese Datei aus dem Cachespeicher bedient werden. Betrachten Sie die Benutzer-Kontrolle zur Seitennavigation, die in Kapitel 6 Benutzer-Kontrollen entwickelt wurde. Ich habe eine Benutzer-Kontrolle erstellt, welche die XML-Datei mit den Navigationslinks liest und ein entsprechendes Navigationsmenü generiert. Jetzt, da die Navigationsstruktur ziemlich sta-
Caching 11 tisch ist, will ich sie allen Benutzern anzeigen. Es wäre eine Verschwendung, die Struktur bei jeder Anfrage dynamisch neu zu erstellen. Daher speichere ich die Navigationsdaten im Cachespeicher und muss diese nur dann erneuern, wenn ich das Navigationsmenü ändere. Ich werde nun die in Kapitel 6 beschriebene Benutzer-Kontrolle modifizieren, um den Cachespeicher nutzen zu können. Der Quellcode für dieses Beispiel befindet sich im Unterverzeichnis Dependencies auf der CD des Buches. Hier ist der Code der XML-Datei, welche die Navigationslinks enthält: Listing 11.3: nav.xml Home default.aspx Masters masters.aspx Transactions Transactions.aspx Trial Balance trialbalance.aspx
Die Benutzer-Kontrolle wird modifiziert, so dass eine Abhängigkeit zwischen dem Cachespeicher und der XML-Datei erstellt werden kann: Listing 11.4: nav.ascx 'Öffentliche Variable für die dargestellte Eigenschaft PUBLIC vGridLines As GridLines
309
ASP.NET 310
11 PUBLIC vBorderColor as String PUBLIC vCellPadding As Integer Sub Page_Load(Src As Object, E As EventArgs) If Not IsPostBack LoadData() End If End Sub Sub LoadData Dim ds As New DataSet Dim fs As filestream Dim xmLStream As StreamReader Dim Source as DataView Source = Cache("MyData") If Source is Nothing fs = New filestream(Server.MapPath("nav.xml"), FileMode.Open, FileAccess.Read) xmlStream = new StreamReader(fs) ds.ReadXML(XmlStream) fs.Close() Source = New DataView(ds.Tables(0)) 'Cache für weitere Nutzung Cache.Insert("MyData", Source, New CacheDependency(Server.MapPath("nav.xml"))) 'Ausdrückliche Generierung der Daten, 'also bitte anzeigen CacheMsg.Text = "Dataset created explicitly" Else CacheMsg.Text = "Dataset retrieved from cache" End If dlist.DataSource=Source dlist.DataBind() dlist.GridLines = vGridLines dlist.BorderColor=System.Drawing.Color.FromName(vBorderC olor) dlist.CellPadding=vCellPadding End Sub
Navigation.aspx ist das Webformular, das die folgende Komponente nutzt: Listing 11.5: Navigation.aspx a {color:black; text-decoration:none;} a:hover {color:red; text-decoration:underline;}
Der größte Teil dieses Codes dürfte Ihnen bekannt vorkommen, da dieser bereits in Kapitel 6 Benutzer-Kontrollen erläutert wurde. Ich habe den Code verschoben, um die LoadData-
311
ASP.NET 312
11 Funktion mit Daten aus dem page_load-Event zu füllen. Die LoadData-Funktion prüft zunächst den Cachespeicher. Wenn die Funktion dort Daten findet, nutzt sie diese zur Bindung der Datenliste (DataList) und zeigt eine entsprechende Meldung an. Im anderen Fall liest die Funktion die XML-Datei, um die Daten zu erhalten, und zeigt eine Meldung über die Erstellung des DataSet an. Wenn Sie also das Webformular, das diese Kontrolle (navigation.aspx) nutzt, zum ersten Mal ausführen, sollten Sie eine Meldung mit dem Inhalt DataSet created explicitly erhalten. Wenn Sie nun Ihren Browser aktualisieren, wird der Datensatz aus dem Cachespeicher aufgefrischt, wobei auch hierzu wieder eine entsprechende Meldung angezeigt wird. Der folgende Code beinhaltet eine Abhängigkeit (Dependency) zwischen dem Cachespeicher und der XML-Datei nav.xml: Cache.Insert("MyData", Source, New CacheDependency(Server.MapPath("nav.xml")))
Jedes Mal, wenn die nav.xml-Datei modifiziert wird, erfolgt jetzt eine erneute Erstellung des DataSet. Um dies zu testen, modifizieren Sie die Datei nav.xml, speichern sie und aktualisieren die Datei navigation.aspx. (Sie müssen die Datei nicht neu starten – drücken Sie einfach den Aktualisierungs-Button ihres Browsers.) Es wird eine Meldung angezeigt, die Ihnen mitteilt, dass ein neuer Datensatz erstellt wurde. Beachten Sie, dass bei einem Klick auf einen der Links in der Navigationsleiste eine Fehlermeldung angezeigt wird, da Sie versuchen, zu einem nicht existenten Webformular zu navigieren.
Zusammenfassung Bei richtiger Anwendung kann der Cachespeicher von ASP.NET die Leistung Ihrer Website erheblich steigern. Der manuelle Batch-Update-Prozess zur Aktualisierung statischer Dateien, die nur gelegentlich modifiziert werden, kann nun durch die Erstellung von Abhängigkeiten zwischen einer XML-Datei und dem Cachespeicher ersetzt werden. Hierdurch werden viele mit der manuellen Intervention verbundene Probleme beseitigt.
Tracing
Page-Level-Tracing
315
Application-Level-Tracing
317
Tracing deaktivieren
318
Zusammenfassung
319
ASP.NET 314
12 Beim Schreiben von Codes ist es häufig hilfreich, die Werte der Variablen anzeigen zu können, um Fehler im Programm aufzuspüren. ASP-Entwickler haben hierzu oft das Response.Write()Statement verwendet. Das Problem hierbei ist, dass die Debugging-Statements bereinigt werden müssen, bevor die Anwendung genutzt werden kann. ASP.NET bietet eine elegantere Lösung des Tracing-Prozesses (Tracing = Aufspüren). Statt Response.Write() benutzen Sie Trace.Write() oder Warn.Write() (zeigt Meldungen in roter Schrift an), um Debugging-Statements zu schreiben. Wenn Sie die Anwendung dann fertig stellen möchten, müssen Sie diese Statements nicht mehr einzeln löschen, sondern lediglich die Trace-Funktion für eine bestimmte Seite oder für die ganze Anwendung deaktivieren. ASP.NET beinhaltet zwei Tracing-Stufen: Page-Level-Tracing und Application-Level-Tracing (Fehlersuche pro Seite oder in der gesamten Anwendung). Page-Level-Tracing wird immer auf eine bestimmte Seite angewandt und muss mit Hilfe des Trace = True-Attributs in der obersten Seitendirektive aktiviert werden. Das Ergebnis wird unten auf der Seite angezeigt. Application-Level-Tracing wird mit Hilfe des Trace-Bereichs der Konfigurationsdatei im Ursprungsverzeichnis aktiviert. Hierbei werden alle Seiten innerhalb einer Anwendung nach Programmierfehlern durchsucht. Mit einer Direktive auf Seiten-Level können jedoch einzelne Seiten von der Suche ausgeschlossen werden. Details hierzu finden Sie in der trace.axd-Utility. Die Trace-Klasse unterstützt zwei überlagernde Methoden: Trace.Write() und Trace.Warn(). Diese Methoden sind identisch, mit dem einzigen Unterschied, dass Trace.Warn() Fehlermeldungen in roter Schrift anzeigt. Hier sind die Prototypen dieser Methoden: Listing 12.1: Visual Basic.NET Public Sub [Warn | Write](category As String, message As String, errorInfo As Exception) End Sub Public Sub [Warn | Write](category As String, message As String) End Sub
Sie können zum Beispiel das folgende Statement im page_load-Event der Seite hinzufügen: Trace.Write("My Trace", "This is Page Load")
Das Ergebnis wird im Tabellenformat angezeigt. Die Spalten der Tabelle sind Category und Message. Im vorigen Fall wird MyTrace in der Category-Spalte und This is Page Load in der Message-Spalte angezeigt. Es ist möglich, die Ergebnistabelle nach Kategorien zu ordnen, so dass alle Meldungen der MyTrace-Kategorie zusammen angezeigt werden.
Page-Level-Tracing Um das Tracing für eine Seite zu aktivieren, muss die folgende Direktive oben auf der Seite eingefügt werden:
Sie können das TraceMode-Attribut verwenden, um die Fehlermeldungen nach Kategorien zu ordnen:
Um nach der Zeit zu sortieren (Grundeinstellung):
Innerhalb des Hauptteils der Seite erhalten Sie so eine Reihe von Trace.Write()- oder Trace.Warn()-Statements. Als Beispiel dient Trace_page.aspx:
315
ASP.NET 316
12 Listing 12.3: Trace_page.aspx Sub Page_Load(objSource As Object, objArgs As EventArgs) Trace.Write("My Trace", "This is Page Load") End Sub Some body matter....
Abbildung 12.1 zeigt das Ergebnis.
Page-Level-Tracing Abb. 12.1
Tracing 12
Application-Level-Tracing Die web.config-Datei wurde bereits in Kapitel 10 ASP.NET-Anwendungen beschrieben. Diese Datei enthält einen Tracing-Bereich, mit dem diese Funktion für die ganze Anwendung aktiviert werden kann, wie dieses Beispiel zeigt:
Dieser Abschnitt verfügt über die folgenden vier Attribute: 1
Enabled = True oder False: Diese Einstellung aktiviert oder deaktiviert das Tracing für die gesamte Anwendung. Das Page-Level-Tracing übersteuert diese Einstellung auf den Seiten, für die es aktiviert wurde.
2
PageOutput = True oder False: Bei True wird das Ergebnis unten auf der Seite angezeigt. Bei False erfolgt keine Fehlermeldung. In diesem Fall muss die trace.axdUtility zum Betrachten der Ergebnisse verwendet werden.
3
RequestLimit = some integer: Die gesamte Anzahl der Anfragen im Memory-Speicher pro Anwendung.
4
TraceMode = SortBycategory oder SortbyTime: Bei der Verwendung von Trace.Write() kann der Benutzer Kategorien spezifizieren. Beispiel: Trace.Write (My Trace, This is Page Load) zeigt die Ergebnisse nach Gruppierungen an, wenn TraceMode auf SortByCategory gesetzt wurde. Die Grundeinstellung ist SortByTime.
Das Samples-Verzeichnis dieses Ordners enthält eine web.config-Datei und ein Webformular namens trace_ application.aspx zur Demonstration des Anwendungs-Tracings. Die web.config-
317
ASP.NET 318
12 Datei wurde bereits beschrieben. Das Webformular trace_ application.aspx entspricht der Webpage, die bereits im Zusammenhang mit dem Page-Level-Tracing erklärt wurde (trace_page.aspx). Der einzige Unterschied besteht darin, dass hier keine Direktive am Anfang der Seite vorhanden ist. Das Tracing wird hier durch die web.config-Datei gesteuert. Denken Sie daran, einen virtuellen Pfad für den Ordner zu erstellen, in den Sie die web.config-Datei und das trace_application.aspxWebformular kopieren. Starten Sie zunächst trace_application.aspx, wobei die PageOutput-Einstellung auf True gesetzt werden muss. Wie vorher erscheint auch hier das Ergebnis unten auf der Seite. Setzen Sie die PageOutput-Einstellung nun auf False. Es wird keine Meldung mehr angezeigt. Um die Fehlermeldungen jetzt zu sehen, müssen Sie trace.axd benutzen. Fordern Sie hierzu trace.axd im gleichen Anwendungsverzeichnis an, in dem auch die Beispielanwendung aufgerufen wurde. Wenn der Anwendungsordner beispielsweise den Namen ASPVirtual hat, müssen Sie den URL http://Localhost/ASPVirtual/trace.axd angeben, um trace.axd aufzurufen. Abbildung 12.2 zeigt das Ergebnis. In dieser Abbildung sehen Sie eine einfache Liste der gefundenen Programmfehler. Um Informationen über einen Fehler zu erhalten, klicken Sie einfach auf den Eintrag. Das Ergebnis entspricht dem des Page-Level-Tracings.
Tracing deaktivieren Wenn Ihre Anwendung fertig gestellt ist, sollten Sie trace.axd durch Hinzufügen des folgenden Eintrags im HttpHandlers-Bereich der config.web-Datei ausdrücklich deaktivieren:
Es ist nicht nötig, die einzelnen Trace.Write()-Statements im Hauptteil des Webformulars in der Anwendung zu löschen.
Tracing 12
319
Betrachten der Ergebnisse mit trace.axd Abb. 12.2
Zusammenfassung Tracing ist ein sehr nützliches Tool für die Suche nach Programmierfehlern. Früher musste man eine Reihe von Response.Write()-Statements im Hauptteil eines Webformulars schreiben, um einen problematischen Code zu debuggen. Diese Statements mussten anschließend wieder entfernt werden, was nicht nur zeitaufwändig war, sondern auch häufig weitere Fehler verursachte, da hierbei unter Umständen auch Programmzeilen gelöscht wurden. Tracing beseitigt dieses Problem, da hierbei die Statements nicht mehr entfernt werden müssen. Es genügt, die Tracing-Funktion zu deaktivieren.
Sicherheit
Formularbasierte Authentifizierung
324
Der Authentifizierungs-Provider Passport
334
Windows-basierte Authentifizierung
335
Zusammenfassung
337
ASP.NET 322
13 Die Sicherheit spielt in Internetanwendungen eine wichtige Rolle. Nicht autorisierte Benutzer müssen von sensiblen Bereichen der Website ausgeschlossen werden. Autorisierten Benutzern muss der Zugang zu diesen Bereichen ermöglicht werden. ASP.NET bietet in Verbindung mit IIS einige exzellente Sicherheitsoptionen. Die Sicherheit in ASP.NET beinhaltet Authentifizierung und Autorisierung. Authentifizierung besteht in der Überprüfung der Daten des Benutzers (Name und Passwort) gegen Authentifizierungs-Provider genannte Autoritäten. Sind die Daten überprüft worden, so gilt der Benutzer als authentifiziert. Im Anschluss daran überprüft der Autorisierungsprozess, ob ein Benutzer zum Zugriff auf eine bestimmte Ressource berechtigt ist. ASP.NET kann die Identität des Benutzers auch zur Ausführung von Codes verwenden. Diesen Vorgang nennt man Impersonation. Sicherheit ist ein dreistufiger Prozess (der dritte Schritt, die Impersonation, ist optional): Benutzerauthentifizierung: Diese Stufe beinhaltet die Überprüfung der Daten eines Benutzers (Name, Passwort) und deren Abgleich mit einem Authentifizierungs-Provider. Benutzerautorisierung: Hierbei wird überprüft, ob der Benutzer die Berechtigung hat, auf eine bestimmte Ressource zuzugreifen. Benutzer-Impersonation: In dieser Stufe führt die Anwendung einen Code unter Verwendung der Identität des Benutzers aus. ASP.NET implemetiert die Authentifizierung durch Authentifizierungs-Provider. Diese Provider sind Module, die einen zur Authentifizierung der Daten des Benutzers benötigten Code enthalten. Zurzeit sind drei Authentifizierungs-Provider verfügbar: Windows-Authentifizierung, Passport-Authentifizierung und Formular-Authentifizierung. Die Windows-Authentifizierung wird in Verbindung mit der IIS-Authentifizierung genutzt. IIS bietet Authentifizierung durch Basic-, Digest- oder Integrated Windows-Authentifizierung. Die Konfiguration der Authentifizierungsoptionen ähnelt dem gleichen Vorgang mit IIS MMC in ASP.
Sicherheit 13 Die Passport-Authentifizierung ist ein zentralisierter Authentifizierungs-Service, der von Microsoft angeboten wird. Er enthält einen einzelnen Sign-In- sowie einen Core-Profile-Service. Die formularbasierte Authentifizierung verwendet Cookies, um Benutzer zu authentifizieren, und ermöglicht Anwendungen, eine eigenständige Datenüberprüfung durchzuführen. Nicht authentifizierte Anfragen werden an ein HTML-Log-inFormular weitergeleitet. Der Benutzer gibt seine Daten ein und sendet die Seite an den Server. Die Anwendung authentifiziert die Anfrage gegenüber einer in einer Datenbank gespeicherten Passwortsammlung, einer XML-Datei oder der web.config-Datei. Wenn der Benutzer authentifiziert ist, erstellt das System einen Cookie mit den Daten des Benutzers. Weitere Anfragen enthalten dann diesen Cookie in der Kopfzeile des Formulars. Um den Authentifizierungs-Service zu aktivieren und den Authentifizierungs-Provider auszuwählen, müssen Sie das -Element im -Bereich der web.configDatei konfigurieren. Diese Datei wurde bereits in Kapitel 10 ASP.NET-Anwendungen beschrieben. Hier ein Beispiel des Sicherheitsbereichs einer Konfigurationsdatei: 'web.config file
Nachdem der Benutzer durch einen der AuthentifizierungsProvider authentifiziert wurde, können Sie ihm weiterhin den Zugang mit Hilfe der NTFS-Kontrollliste verweigern oder ihm Zugang zu bestimmten Bereichen der Website durch Nutzung der Genehmigungen in der web.config-Datei gewähren. ASP.NET unterstützt die rollenbasierte Sicherheit. Benutzer können auf der Basis ihrer NT-Benutzer-/Gruppenkonten oder durch benutzerdefinierte Benutze-/Gruppeninformationen aus einer Datenbank oder Textdatei authentifiziert werden.
323
ASP.NET 324
13
Formularbasierte Authentifizierung Die formularbasierte Authentifizierung ist die flexibelste Form der Authentifizierung, da sie es Ihnen ermöglicht, Ihre eigenen HTML-Log-in- Formulare zu erstellen und Ihnen damit die größtmögliche Kontrolle über den Authentifizierungsprozess gibt. Dieser Prozess wird an zwei Beispielen erklärt. Das erste ist ein vereinfachtes Beispiel, das Benutzernamen und Passwörter in der web.config-Datei speichert. Hierdurch erhalten Sie einen schnellen Einblick in den cookiebasierten Authentifizierungsprozess. Das zweite Beispiel ist etwas funktioneller und für Produktionsseiten relevant. Anhand dieses Beispiels lernen Sie, Passwörter gegenüber in einer Datenbank gespeicherten Daten zu authentifizieren.
Ein einfaches Beispiel für eine formularbasierte Authentifizierung Dieses Beispiel enthält drei Dateien: web.config, default.aspx und login.aspx. Diese Dateien befinden sich im Beispielordner für dieses Kapitel auf der CD des Buches. Erstellen Sie einen virtuellen IIS-Ordner (um das Beispiel als ASP.NET-Anwendung zu markieren) und kopieren Sie die drei Dateien dort hinein. Der Sicherheitsbereich der web.config-Datei wird folgendermaßen konfiguriert: Listing 13.1: web.config
Sicherheit 13
Beachten Sie, dass diese Datei drei Bereiche hat: Authentication, Authorization und Identity. Die Bedeutung dieser Elemente wird in den folgenden Abschnitten erklärt.
Wie bereits erwähnt, kann zwischen den Modi Windows (Grundeinstellung), Forms, Passport oder None gewählt werden.
Die name-Einstellung gibt den Namen des Cookies an und loginurl spezifiziert den Namen des aspx login-Formulars (in diesem Fall login.aspx). Das Path-Attribut spezifiziert den Pfad, für den der Cookie gültig ist. path="/" kennzeichnet die komplette Seite. Das Sicherheitsattribut kann aus All, None, Encryption oder Validation ausgewählt werden. All ist voreingestellt und verwendet gleichermaßen Datenvalidierung und Verschlüsselung. :
Berechtigungsnachweise (Name, Passwort) werden so gespeichert, dass Benutzer auf die Anwendung in diesem Bereich zugreifen können. In diesem Beispiel ist die Speicherung von Berechtigungsnachweisen nicht erforderlich; daher wird dieser Vorgang im nächsten Beispiel erklärt. Als Einstellung für PasswordFormat sind Clear, SHA1 oder MD5 möglich. Clear bedeutet, dass das Passwort als reiner Text gespeichert wird und Benutzereingaben direkt mit diesem Wert verglichen werden, ohne dass eine weitere Übertragung stattfindet. SHA1 und MD5 kennzeichnen streugespeicherte Algorithmen, die für die Passwörter verwendet werden. Die SHA1-Verschlüsselungsmethode speichert das Passwort im SHA1-Textreferat. Die MD5-Verschlüsselungsmethode wird im
325
ASP.NET 326
13 MD5-Streutextreferat gespeichert. Normalerweise werden Passwörter in einer Datenbank gespeichert. Um das Passwort vor ungewollten Einblicken zu schützen, kann es verschlüsselt werden (mit streugespeicherten SHA1- oder MD5-Algorithmen). Hierzu kann die HashPasswordForStoringInConfigFileMethode angewendet werden, die in der FormsAuthentication-Klasse gespeichert ist. Diese Methode akzeptiert den Passwort-String und die Verschlüsselungsmethoden und gibt das verschlüsselte Passwort zurück. Hier ein Beispiel: Listing 13.2: encrypt.aspx Sub Page_Load(Src As Object, E As EventArgs) Dim SHA1Password As string Dim MD5Password As string SHA1Password = FormsAuthentication.HashPasswordForStoringInConfigFile ("Hersh","SHA1") MD5Password = FormsAuthentication.HashPasswordForStoringInConfigFile ("Hersh","MD5") response.write( "Password in SHA1 Format : " + SHA1Password + "" ) response.write("Password in MD5 Format : " + MD5Password) End Sub
In diesem Beispiel werden zwei Methoden angewendet, um den Benutzernamen Hersh zu verschlüsseln. Hier ist das Ergebnis: Password im SHA1 Format: BBB0E8F21D0747A3A4927477F86886F1AE6FBE78 Password im MD5 Format: 52F79F51B8A443BC6F915A585BB0C1FF
Im Normalfall wird das verschlüsselte Passwort in der Datenbank oder der web.config-Datei gespeichert. Die HashPasswordForStoringInConfigFile-Methode kann Passwörter nur
Sicherheit 13 verschlüsseln, nicht aber verschlüsselte Passwörter als normalen Text darstellen. Hierzu können Sie die .NET-ChiffrierungsAPIs im SystemSecurity.Cryptography-Namensfeld verwenden. DevPower Solutions bietet eine freie Version der .NET-Chiffrierungskomponente unter http://www.devpower.com/Encrypt. NET an. Die Unterbereiche Password und Username spezifizieren die autorisierten Benutzer der Anwendung sowie deren Passwörter.
Der Autorisierungsbereich verfügt über zwei Unterbereiche namens und , die dazu dienen, einen Zugang zu genehmigen oder zu verweigern. Um zum Beispiel anonymen Benutzern den Zugang zu verweigern, kann die spezielle Identität ? benutzt werden: .
Um allen Benutzern den Zugang zu gewähren, kann die *-Identität eingesetzt werden:
Die loginurl-Einstellungen des Cookie-Unterbereichs spezifizieren das zu verwendende Login-Formular, in diesem Beispiel also login.aspx: Listing 13.3: Login.aspx Sub Login_Click(Src As Object, E As EventArgs) if FormsAuthentication.Authenticate(UserName.Value, UserPass.Value) FormsAuthentication.RedirectFromLoginPage(UserName.Value , UserPass.Value) Else Msg.Text = "Sorry Invalid username or password : Please try again" End If End Sub
327
ASP.NET 328
13 Login Page
User Name:
Password:
Persistent Cookie:
Der Hauptteil des Formulars enthält zwei Textboxen: UserName und UserPassword sowie eine Checkbox PersistCookie. Wenn diese Checkbox markiert ist, bleibt der Cookie bestehen, das heißt, dass die Information nicht verloren geht, wenn der Browser geschlossen wird.
Sicherheit 13 Wenn der Benutzer auf den Login-Button klickt, wird das login_click-Event ausgelöst. Dieses erste Event benutzt die Authenticate-Methode der FormsAuthentication-Klasse, um die Daten des Benutzers zu authentifizieren. Wenn der Benutzer authentifiziert ist, wird die RedirectFromLoginPage-Methode verwendet, um den Cookie zu speichern (und um ihn beizubehalten, wenn die PersistCookie-Checkbox markiert wurde) und um den Benutzer zu der ursprünglich von ihm angeforderten Seite weiterzuleiten. Das default.aspx-Formular beinhaltet das Formular, welches der Benutzer ursprünglich angefordert hat und das ihm nach dem Login-Prozess angezeigt wird. Hier ist die entsprechende Codeliste: Listing 13.4: default.aspx Sub Page_Load(Src As Object, E As EventArgs) msg.Text = "Welcome, " + User.Identity.Name End Sub Sub Signout_Click(Src As Object, E As EventArgs) CookieAuthentication.SignOut() Response.Redirect("login.aspx") End Sub Using Cookie Authentication
Im page_load-Event dieser Seite wird mit der User-Klasse auf den Benutzernamen zugegriffen: msg.Text = "Welcome, " + User.Identity.Name
329
ASP.NET 330
13 Die User-Klasse ist aus einer Website heraus verfügbar und hat drei Methoden: IsAuthenticated, Name und Type. Die IsAuthenticated-Methode gibt einen Boolean-Wert zurück, der definiert, ob ein Benutzer authentifiziert wurde. Die NameMethode gibt den Benutzernamen zurück und die Type-Methode zeigt die Art der Authentifizierung an: Sub Signout_Click(Src As Object, E As EventArgs) CookieAuthentication.SignOut() Response.Redirect("login.aspx") End Sub
Abbildung 13.1 zeigt einen Screenshot der Login-Seite.
Die Login-Seite Abb. 13.1
Abbildung 13.2 zeigt einen Screenshot des erfolgreichen Logins.
Sicherheit 13
331
Ein erfolgreicher Login Abb. 13.2
Speicherung eines Passworts in einer Datenbank In diesem Beispiel werden Sie lernen, die formularbasierte Authentifizierung mit Passwörtern, die mit einer Datenbank verglichen werden, zu benutzen. Ich speichere die Passwörter in einer Zugriffsdatenbank namens security.mdb. Die Implementierung besteht aus drei Dateien: web.config, default.aspx und login.aspx. Diese Dateien befinden sich im Advanced-Verzeichnis des Beispielordners für dieses Kapitel auf der CD des Buches. Die web.config- und die default.aspx-Datei sind mit den bereits beschriebenen Dateien gleichen Namens identisch. Der einzige Unterschied besteht darin, dass in dieser web.configDatei keine Benutzerdaten gespeichert werden. Die einzige unterschiedliche Datei heißt login.aspx, deren Code hier aufgelistet ist: Listing 13.5: login.aspx (database version)
ASP.NET 332
13 Sub Login_Click(Src As Object, E As EventArgs) Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet Dim ConnStr As String Dim SQL As String Dim dv As DataView ConnStr = "PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=" & server.mappath("security.mdb") myConnection = New OleDbConnection(ConnStr) 'DataSet-Befehl SQL = "select * from passwords" myCommand = New OleDbDataAdapter(SQL, myConnection) 'Benutze Fill-Methode des DataSetCommand, um einen Datensatz zu füllen myCommand.Fill(ds, "passwords") dv = new DataView(ds.Tables("passwords")) dv.Sort = "UserName" dv.RowFilter = "UserName = '" + UserName.Value + "' and password = '" + UserPass.Value +"'" if dv.count >=1 then FormsAuthentication.RedirectFromLoginPage(UserName.Value , PersistCookie.Checked) Else Msg.Text = "Sorry Invalid username or password : Please try again" End if End Sub Login Page
User Name:
Sicherheit 13
Password:
Persistent Cookie:
In diesem Beispiel wird ein DataSet mit Reihen aus der Passwort-Tabelle der Datei security.mdb gefüllt. Anschließend wird die voreingestellte Ansicht des DataSet einem DataView zugeordnet und der Benutzername und das Passwort herausgefiltert: dv.Sort = "UserName" dv.RowFilter = "UserName = '" + UserName.Value + "' and password = '" + UserPass.Value +"'"
Anschließend wird die Anzahl der Reihen mit der count-Eigenschaft der Datenansicht (DataView) überprüft. Ist der Wert dieser Eigenschaft größer als 1, wird dem Benutzer der Zugang gewährt. In jedem anderen Fall werden die Daten zurückgewiesen: if dv.count >=1 then FormsAuthentication.RedirectFromLoginPage(UserName.Value , PersistCookie.Checked)
333
ASP.NET 334
13 Else Msg.Text = "Sorry Invalid username or password : Please try again" End if
Der Authentifizierungs-Provider Passport Der Authentifizierungs-Provider Passport ist ein zentraler Authentifizierungs-Service, der von Microsoft angeboten wird. Er enthält single sign-in- und core-profile-Services für Mitgliederseiten. Wenn Sie einen Hotmail-Account besitzen, wird Passport verwendet, um Sie zu authentifizieren. Gerade zu diesem Zeitpunkt hat Microsoft neue Informationen und ein Weißbuch bezüglich Hailstorm veröffentlicht. Das Produkt ist eine wichtige Technologiekomponente von Microsofts .NET-Vision und wird Webversionen von Hotmail, MSN Messenger und dem Authentifizierungsprodukt Passport enthalten. Passport wird als eine Art Personalausweis für das Internet dienen. Ihre kritischen persönlichen Daten werden an einem zentralen Ort gespeichert. Sie haben zu jedem Zeitpunkt die volle Kontrolle über diese Daten. Dadurch können Sie für alle Partner-Websites den gleichen Benutzernamen und das gleiche Passwort benutzen. Das Gleiche gilt für Ihre Adressbücher und Ihre Bookmarks. Diese sind dann von jedem Computer der Welt aus über das Web zugänglich und würden nur einmal erstellt. Der allgemeine Implementierungsprozess der Passport-Authentifizierung läuft folgendermaßen ab: 1
Laden Sie Passport SDK von der Site http://www .passport.com/business herunter und installieren Sie das Programm. Sie müssen sich hierfür registrieren und Gebühren zahlen.
2
Konfigurieren Sie Passport als den Authentifizierungsmodus in der Konfigurationsdatei:
Sicherheit 13 3
Implementieren Sie die Passport-Authentifizierung und die Autorisierung entsprechend den Anweisungen in der Passport-Dokumentation.
Dieses Programm erfordert die Passport-Registrierung der Benutzer einer Website. Benutzer können sich durch Einrichtung eines Hotmail- oder MSN-Accounts registrieren. Sie können auch direkt auf die Passport-Seite gehen und sich dort registrieren. Wenn ein Benutzer eine geschützte Ressource anfordert und die Anfrage kein gültiges Passport-Ticket enthält, so wird eine 302-Fehlermeldung angezeigt und der Benutzer auf die Passport-Seite zurückgeleitet. Die verschlüsselten Parameter der ursprünglichen Anfrage werden mit dem Anfrage-Service an Passport weitergeleitet. Passport präsentiert ein LoginFormular, das die geforderten Daten über SSL (Secure Socket Layers) zurücksendet. Der Passport-Loginserver authentifiziert den Benutzer und leitet ihn zu der ursprünglichen URL zurück, wobei das Authentifizierungs-Ticket im Anfragestring verschlüsselt wird. Der Server entdeckt das Fehlen eines Cookies und die Existenz des Authentifizierungs-Tickets im Anfragestring und erstellt daraufhin einen Authentifizierungs-Cookie. Nachfolgende Anfragen an die Ressource werden durch das gelieferte Ticket authentifiziert.
Windows-basierte Authentifizierung Windows oder IIS bieten eine geradlinige Authentifizierung. Aktivieren Sie einfach die IIS-Basisauthentifizierung im IIS MMC. Geben Sie an, welchen Benutzern Zugang zu einer bestimmten Webanwendung gewährt werden soll, und legen Sie Windows als Authentifizierungsmodus in der Konfigurationsdatei fest.
Aktivierung der Basissicherheitsauthentifizierung Im folgenden Beispiel wird Windows NT verwendet:
335
ASP.NET 336
13 1
Öffnen Sie IIS MMC. Klicken Sie mit der rechten Maustaste auf Default Website und wählen Sie Properties/Directory/Security/Edit.
2
Markieren Sie die Basic Authentication-Checkbox wie in Abbildung 13.3 gezeigt
Konfiguration der Genehmigungen Nun müssen Sie noch ACL (access control lists) auf der Basis der Gruppen oder Benutzer Ihrer Anwendung einstellen. 1
Wählen Sie den physischen Ordner Ihrer Anwendung (nicht den virtuellen Ordner). Klicken Sie mit der rechten Maustaste darauf und wählen Sie das Register Security. In Windows NT sehen Sie den Bildschirm nun wie in Abbildung 13.4 gezeigt.
2
Konfigurieren Sie ACL auf der Basis der Gruppen oder Benutzer Ihrer Anwendung.
Konfigurieren der Genehmigung in Windows NT Abb. 13.3
Sicherheit 13
Aktivierung der Basissicherheit Abb. 13.4
Editieren der Web.config-Datei Modifizieren Sie den Sicherheitsbereich der web.config-Datei, um die Windows-Authentifizierung zu aktivieren:
Wenn ein Benutzer versucht, auf eine Ressource in der Anwendung zuzugreifen, wird er gebeten, sich einzuloggen, wie in Abbildung 13.5 zu sehen ist.
Zusammenfassung ASP.NET bietet in Kombination mit IIS einige nützliche Sicherheits-Features. Die formularbasierten Sicherheitsoptionen bieten größtmögliche Flexibilität. Die Beispiele aus diesem Kapitel können ohne weiteres in echten Websites verwendet werden. Der Authentifizierungs-Provider Passport muss noch
337
ASP.NET 338
13
Windows-basierter Login Abb. 13.5
auf seine Akzeptanz geprüft werden. Zurzeit wird dieser Provider von Firmen wie American Express, Click Commerce, eBay, Expedia.com und Groove Networks benutzt. Glaubt man den Visionen von Microsoft, nach denen Passport eine wichtige Rolle im .NET-Design spielen soll, werden sicherlich noch weitere Unternehmen folgen.
Projekt 1: Webservices
ASP.NET 340
Projekt 1 – Überblick Mit ASP.NET und Webservices könnten Sie Anwendungen erschaffen, die Daten senden und empfangen können, indem sie das Internet und das HTTP-Protokoll benutzen. Sie könnten z.B. eine persönliche Finanzbuchhaltungssoftware webfähig machen und über das Internet benutzen. Die Konsequenzen dieser Betrachtungsweise können sich über weite Gebiete erstrecken. Stellen Sie sich einen Fall vor, bei dem ein Automobilhersteller auf eine große Anzahl von Zulieferfirmen angewiesen ist, die ihn mit Einzelteilen wie Luftfiltern, Scheibenwischern, Reifen usw. beliefern. Diese Zulieferunternehmen wären nun über die gesamte Welt verstreut. Dieser Automobilhersteller arbeitet mit Just in time-Fertigungstechniken und die Zulieferfirmen produzieren (und beliefern) nur so viel, wie der Hersteller in einer bestimmten Periode verbraucht. Dadurch wird beiderseitig die Lagerung des Inventars vermieden. Nehmen wir an, ein ausländisches Unternehmen fertigt Luftfilter für den Automobilhersteller. Es würde tagtäglich aktuelle Informationen über den Luftfilterverbrauch des Automobilhersteller benötigen, um die eigene Herstellung dementsprechend anzupassen. Dafür bräuchte der Zulieferer Zugriff auf den täglichen Produktionsbedarf des Automobilherstellers, der andererseits auf den Zugriff auf die Produktions- und Inventarzahlen des Zulieferunternehmens im Ausland angewiesen ist. Müssten Sie dafür eine Satellitenverbindung zwischen den beiden Unternehmen aufbauen, um ihre Datenbanken miteinander verknüpfen zu können? Eine Einzelhandelskette hat Filialen über das ganze Land verteilt. Jedes Mal, wenn ein Kunde einen Erwerb tätigt, muss der verkaufte Artikel aus dem Inventar gelöscht werden und der Geldbetrag in den Finanzbüchern als Umsatz verbucht werden. Angenommen, alle einzelnen Filialen dieser Kette greifen auf dieselbe Datenbank zu. Würden Sie dafür eine Netzwerkinfrastruktur aufbauen wollen? Und was würde das für Kosten verursachen? In den beiden vorangegangenen Beispielen müssen wir Verbindungen zwischen verschiedenen Datenbanken an weit entfernten Orten aufbauen. Um dies zu erreichen, würden wir herkömmlicherweise eine Netzwerkinfrastruktur aufbauen.
Projekt 1: Webservices 341 Sie könnten die Datenbanken jedoch auch mit Webservices über das Internet miteinander verlinken. Eine Anwendung, die mit einer Datenbank interagiert, muss grundsätzlich die Möglichkeit bieten, Datensätze von der Datenbank zu selektieren, einzufügen, zu aktualisieren und zu löschen. Mit Hilfe von Webservices können wir die Datenbankinteraktionslogik über eine Folge von Methodenaufrufen über das Internet erreichen. Wenn wir die Fähigkeit haben, über das Web auf die Datenbank zuzugreifen, bräuchten wir keine teuren Satelliten- oder Direktverbindungen zwischen den Anwendungen und den Datenbanken. Eine Benutzerschnittstelle im Browser kann durch die ASP.NETTechnologie mit Datenbanken kommunizieren. Durch weitere Tools, wie Visual Basic.NET und C#, können Sie Anwendungen erstellen, die benutzerfreundlich sind und eine ansprechendere Benutzerschnittstelle bieten. Benutzer haben sich zum Beispiel daran gewöhnt, Symbolleisten, MDI (multiple document interface) und andere graphische Leckerbissen in Anwendungen vorzufinden. Mit einem Browser können Sie das natürlich nicht erzeugen. Wäre es nicht schön, wenn Sie eines dieser Entwicklungstools einsetzen und trotzdem über das Internet kommunizieren könnten? Wir würden dann in der Lage sein, Anwendungen mit einer umfangreichen graphischen Oberfläche zu erstellen, ohne aufwändige Netzwerksysteme aufbauen zu müssen. Genau dafür gibt es die Webservices. In diesem Projekt werde ich Ihnen zeigen, wie Sie generische Datenbankzugriffsdienste erstellen und diese dann in Visual Basic.NET-, C#oder ASP.NET-Anwendungen einbinden können. Sie werden außerdem einen persönlichen Finanzbuchhalter erstellen, der diesen Webservice mit ASP.NET verwenden wird. Sie können eine moderne Anwendung (mit all Ihren graphischen Feinheiten) in Visual Basic.NET oder C# entwickeln und weiterhin diesen Webservice verwenden.
Aufbau eines generischen Datenbank-Webservices
ASP.NET 344
14 In Kapitel 8 Business-Objekte wurde Ihnen gezeigt, wie Sie ein generisches Datenbank-Objekt erzeugen können, um mit einer Datenbank zu kommunizieren. Dieses Objekt hatte die Möglichkeit, Datenbankabfragen wie Insert, Update und Delete auszuführen. Basierend auf SQL-Benutzerabfragen konnte dieses Objekt auch eine allgemeine Routine ausführen, um einen Datensatz zurückzugeben. Dieser Datensatz konnte dann dazu benutzt werden, um ein ASP.NET-Servercontrol zu binden. Ich werde Ihnen zeigen, wie Sie dieses Business-Objekt in einen Webservice konvertieren können. Verschiedenste Buchhaltungsmodule können dann auf die Funktionen zugreifen, die durch den Webservice gegeben sind. Ich werde Sie nun durch den Prozess der Erstellung eines Webservices leiten. Dazu sind einige Schritte notwendig: 1
Erstellen Sie die asmx-Datei für den Webservice: Benutzen Sie dafür NotePad und legen Sie eine Datei mit dem Namen SQLService.asmx und folgendem Inhalt an:
Listing 14.1: SQLService.asmx Imports System Imports System.Web.Services Imports System.Data Imports System.Data.OleDb Imports System.Text Public Class SQLService: Inherits WebService Public Function TestFunction (vInput as Boolean) As String If (vInput = TRUE) Then TestFunction = "It is the truth..." Else TestFunction = "False!False!False" End if End Function Public Function add( a as integer, b as integer) as string add = cstr(a+b) End function
Aufbau eines generischen Datenbank-Webservices 14
Public Function Populate(ConnStr as string, SQL as string) As DataSet Dim dv As DataView Dim i As integer Dim myConnection As OleDbConnection Dim myCommand As OleDbDataAdapter Dim ds As New DataSet myConnection = New OleDbConnection(ConnStr) myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "vTable") 'Populate = ds.Tables("vTable").DefaultView Populate = ds End Function PUBLIC Function RunSql ( ConnStr as string, vsql as string) as String Dim Message As string try message = "Success" Dim myConnection As OleDbConnection myConnection = New OleDbConnection(ConnStr) Dim mycommand As New OleDbCommand(vsql,myConnection) myConnection.Open() myCommand.ExecuteNonQuery() myConnection.Close() Catch ex As OleDbException Dim errItem As OleDbError Dim errString As String For Each errItem In ex.Errors errString += ex.Message + " " Next Message = "SQL Error.Details follow:" & errString Catch myException as Exception message = "Exception: " + myException.ToString() End try RunSql = message End Function End Class
345
ASP.NET 346
14 Das ist genau der gleiche Code, den wir gemeinsam in Kapitel 8 Business Objekte erarbeitet haben, deswegen werde ich ihn hier nicht noch einmal im Detail besprechen. Die Unterschiede sind, dass die Datei eine asmxEndung hat, die Klasse von WebService erbt, ein WebService-Attribut in der Kopfzeile eingefügt wurde, der System.Web.Services-Namensraum (namespace) importiert wurde und jede Funktion mit einem -Tag gekennzeichnet wurde. Das WebMethodTag signalisiert der ASP.NET-Runtime, dass die entsprechende Methode über das Internet aufgerufen werden kann. 2
Erstellen Sie die WSDL-Datei: Öffnen Sie SQLService.asmx, so dass es durch IIS aufgerufen wird (wie zum Beispiel http://localhost/Ihr_virtueller_Ordner/SQLService.a smx). Klicken Sie auf Zeige WSDL. Speichern Sie die erzeugte Datei als SQLService.wsdl. Beachten Sie, dass Sie das Gleiche auch erreichen können, wenn Sie http://localhost/Ihr_virtueller_Ordner/SQLService. asmx?wsdl im Browser aufrufen.
3
Erstellen und kompilieren Sie den Proxy. Führen Sie msqlProxy.bat aus, welches den Proxy SQLService.cb im lokalen Ordner erstellt und die kompilierte DLL SQLService.dll im bin-Ordner erstellt.
Listing 14.2: MsqlProxy.bat REM ------------Erstelle Proxy-----------wsdl.exe /l:VB /n:NameSpaceHersh /out:SqlService.vb SqlService.wsdl REM ------------Kompiliere Proxy-----------Rem Bitte ändern Sie die outdir-Variable, so dass sie auf Ihren bin-Ordner verweist. set outdir=g:\AspNetSamples\bin\SQLService.dll set assemblies=System.dll,System.Web.dll,System.Data. dll,System.Web. Services.dll,System.Xml.dll vbc /t:library /out:%outdir% /r:%assemblies% SQLService.vb pause
Aufbau eines generischen Datenbank-Webservices 14 4
Testen Sie den Service: Das Formular, das ich geschrieben habe, um diesen Service zu testen, heißt SQLService.aspx und hat den folgenden Code:
Listing 14.3: SQLService.aspx Dim SQL as string, vcn as string Sub Page_Load(Source As Object, E As EventArgs) vcn= "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=ASPNET;User ID=sa;" if NOT (isPostBack) rebind end if End Sub Sub Show_Click(Sender As Object, E As EventArgs) Message.Text = "Masters-Tabelle angezeigt... " ReBind End Sub Sub Insert_click(Sender As Object, E As EventArgs) sql = "Insert into Masters(code_display,code_category,type)" sql = sql + "Values ('test',701,'E')" RunSQL(sql) rebind Message.Text = " ..Test-Aufzeichnung eingefügt... " End Sub Sub Delete_click(Sender As Object, E As EventArgs) sql = "delete from masters where code_display = 'test'" RunSQL(sql) rebind
347
ASP.NET 348
14 Message.Text = "...alle Test-Aufzeichnungen gelöscht..." End Sub Sub Update_Click(Sender As Object, E As EventArgs) sql = "UPDATE Masters Set Opening = 90 WHERE code_display = 'test'" RunSQL(sql) rebind Message.Text = "...alle TestAufzeichnungen aktualisiert: Setze closing balance = 90...! " End Sub Sub ReBind() Dim t As NameSpaceHersh.SqlService = New NameSpaceHersh.SqlService() Dim vsql as string Dim ds as DataSet vSQL = "select * from Masters" ds = t.Populate(vcn, vSql) DataGrid1.DataSource=ds.Tables("vTable").DefaultV iew DataGrid1.DataBind() End Sub Function RunSQL (vSQL as String) Dim t As NameSpaceHersh.SqlService = New NameSpaceHersh.SqlService() t.RunSQL(vcn,vSQL) End Function ASP .NET Webservices
Aufbau eines generischen Datenbank-Webservices 14
In dem Page_load-Event spezifiziere ich den Connection-String als vcn= "Provider=SQLOLEDB; Data Source=(local);Initial Catalog=ASPNET;User ID=sa;"
Der Sub ReBind ruft die Populate-Funktion des Webservices auf und übergibt ihr die Query select * from Masters sowie den Connection-String. Die Populate-Funktion gibt einen Datensatz zurück, dessen Default View an das DataGrid wie folgt gebunden ist: Sub ReBind() Dim t As NameSpaceHersh.SqlService = New NameSpaceHersh.SqlService() Dim vsql as string Dim ds as DataSet vSQL = "select * from Masters" ds = t.Populate(vcn, vSql) DataGrid1.DataSource=ds.Tables("vTable").DefaultView DataGrid1.DataBind() End Sub
Beachten Sie, dass dies eine ziemlich geschickte Art ist, Abfragen aufzurufen, die Daten zurückgeben. Sie können jede SQLAbfrage (die Abfrage kann auch eine Verknüpfung auf multiple Tabellen sein) übermitteln und einen Datensatz zurückbekommen, der dann beliebig manipuliert werden kann. Sie können auch eine Verbindung zu jeder beliebigen Datenbank aufbauen, da der Connection-String der Datenbank an die Funktion weitergegeben wird. Die drei Buttons habe ich mit Click-Events versehen, die ein insert-, ein update- und ein delete-Statement an die entsprechende RunSQL-Funktion des Formulars übermitteln. Diese ruft daraufhin die RunSQL- Funktion des Webservices auf. Hier sind die drei Funktionen:
349
ASP.NET 350
14 Sub Insert_click(Sender As Object, E As EventArgs) sql = "Insert into Masters(code_display,code_category,type)" sql = sql + "Values ('test',701,'E')" RunSQL(sql) rebind Message.Text = " .. Test-Aufzeichnungen eingefügt... " End Sub Sub Delete_click(Sender As Object, E As EventArgs) sql = "delete from masters where code_display = 'test'" RunSQL(sql) rebind Message.Text = "...alle Test-Aufzeichnungen gelöscht..." End Sub Sub Update_Click(Sender As Object, E As EventArgs) sql = "UPDATE Masters Set Opening = 90 WHERE code_display = 'test'" RunSQL(sql) rebind Message.Text = "...Alle Test Aufzeichnungen Aktualisiert: Setze closing balance = 90...! " End Sub
Die lokale RunSQL-Funktion ruft die RunSQL-Funktion des Webservices auf und übergibt ihr den SQL-String und den Connection-String. Die Webservice-Funktion führt daraufhin den Aktionsaufruf (action query) aus. Function RunSQL (vSQL as String) Dim t As NameSpaceHersh.SqlService = New NameSpaceHersh.SqlService() t.RunSQL(vcn,vSQL) End Function
Webservices sind ein wichtiger Bestandteil von ASP.NET. Die in diesem Kapitel entwickelten Methoden zeigen, wie der Prozess der Datenbankinteraktion als ein Webservice abstrahiert und verkapselt werden kann. In den folgenden Kapiteln dieses Projekts werde ich Ihnen erklären, wie sich die in diesem Kapitel entwickelten Methoden für eine Buchhaltungsanwendung umsetzen lassen.
Gestaltung eines Navigationssystems
Navigationslinks
352
Die Benutzersteuerung
353
Die Steuerung verwenden
354
ASP.NET 352
15 In Kapitel 6 Benutzer-Kontrollen gestalteten Sie ein XML-basiertes Navigationssystem für eine Website. Sie entwickelten eine Benutzersteuerung für eine Webseite, die Navigationslinks generierte. In Abbildung 15.1 können Sie sich anschauen, wie diese Navigationslinks aussehen. Die Navigationslinks wurden in einer XML-Datei gespeichert. Ich werde noch einmal kurz die Benutzersteuerung erklären, da ich sie in jedem Formular dieses Projektes verwenden werde.
Navigationslinks Die Navigationslinks dieses Projektes sind in der Datei nav.xml gespeichert. Listing 15.1: Nav.xml Home default.aspx Masters masters3.aspx Transactions selection.aspx Trial Balance Trialbalance.aspx
Jeder Link, der angezeigt werden soll, ist in dem Site-Knoten integriert. Dieser hat zwei Elemente: den Site-Namen und den Site-URL. Meine Benutzersteuerung zeigt jeden dieser URLs am oberen Ende jeder Seite an.
Gestaltung eines Navigationssystems 15
Navigationslinks Abb. 15.1
Die Benutzersteuerung Die Benutzersteuerung wird in Kapitel 6 Benutzer-Kontrollen beschrieben. Um es noch einmal kurz anzureißen: Sie können ihr die GridLines-, BorderColor- und CellPadding-Merkmale zuweisen. Sie liest eine XML-Datei und bindet eine DataList an sich. Die DataList zeigt die Links, die man auf jeder Seite vorfinden kann. Hier ist der Code: Listing 15.2: nav.ascx 'Public Variable for each exposed Property PUBLIC vGridLines As GridLines PUBLIC vBorderColor as String PUBLIC vCellPadding As Integer Sub Page_Load(Source As Object, E As EventArgs) Dim ds As New DataSet Dim fs As filestream Dim xmLStream As StreamReader
353
ASP.NET 354
15 fs = New filestream(Server.MapPath("nav.xml"), FileMode.Open, FileAccess.Read) xmlStream = new StreamReader(fs) ds.ReadXML(XmlStream) fs.Close() dlist.DataSource=ds.Tables("site").DefaultView dlist.DataBind() dlist.GridLines = vGridLines dlist.BorderColor=System.Drawing.Color.FromName(vBorderC olor) dlist.CellPadding=vCellPadding End Sub
Die Steuerung verwenden Jede Webseite muss diese Steuerung registrieren, indem sie die folgende Deklaration am Anfang jeder Seite einbindet.
Gestaltung eines Navigationssystems 15 Innerhalb der Seite wird die Steuerung wie folgt aufgerufen:
355
Integration von Webservices in die Felder eines Buchhaltungsformulars
ASP.NET 358
16 Man könnte einen persönlichen Finanzbuchhaltungs-Manager so entwickeln, dass er ein Webformular zusammen mit einem VB-Code-Behind- Formular verwendet. In diesem Kapitel werde ich jedoch zeigen, wie man den SQLService-Webservice verwendet. Die beiden Methoden des VB-Code-Behind-Formulars, die mit der Datenbank interagieren würden, müsste man so gestalten, dass sie den Webservice benutzen würden. Es sind die Methoden Rebind und RunSQL. Normalerweise würde man im Masters.vb in der ReBind()-Methode ein DataGrid und eine DropDownList an eine Datenbanktabelle binden. Hier ist der Codeauszug, den ich danach verändern will: Listing 16.1: Zu verändernde ReBind-Methode Sub ReBind() SQL = "select m.*, g.code_display as category " SQL = SQL + "from masters m, groups g " SQL = SQL + " where m.code_category = g.code_value" myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "masters") 'Ein Grid wird gebunden Grid1.DataSource=ds.Tables("masters").DefaultView Grid1.DataBind() SQL = "Select * from groups order by code_display" myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "groups") 'populate drop down list acode_category.DataSource=ds.Tables("groups").DefaultView acode_category.DataBind() hidePanel() End Sub
Um nun den Webservice zu benutzen, habe ich den Code folgendermaßen abgeändert: Listing 16.2: Die veränderte ReBind-Methode, welche den Webservice benutzt Sub ReBind() Dim t As New NameSpaceHersh.SQLService Dim ds As DataSet ' Bind Grid SQL = "select m.*, g.code_display as category "
Integration von Webservices 16 SQL = SQL + "from masters m, groups g " SQL = SQL + " where m.code_category = g.code_value" ds = t.Populate(ConnStr, SQL) Grid1.DataSource=ds.Tables("vTable").DefaultView Grid1.DataBind() 'populate drop down list SQL = "Select * from groups order by code_display" ds = t.Populate(ConnStr, SQL) acode_category.DataSource=ds.Tables("vTable").DefaultView acode_category.DataBind() hidePanel() End Sub
In der modifizierten ReBind-Methode rufe ich die WebserviceMethode Populate zweimal auf. Jedes Mal übergebe ich dabei einen Connection-String und einen SQL-Aufruf. Die Funktion gibt einen Datensatz zurück, der das Ergebnis enthält. Das benutze ich, um das DataGrid und die DropDownList-Schnittstelle daran zu binden. Die zweite Funktion, die daran gebunden sein muss, ist RunSQL. Diese Funktion wird dazu verwendet, eine Zeile hinzuzufügen, zu löschen oder zu aktualisieren. Die entsprechenden Eventhandler übergeben dieser Funktion eine SQL Action Query oder einen gespeicherten Prozedurnamen mit den jeweiligen Parametern. Hier ist der VB-Auszug: Listing 16.3: Die VB-Methode RunSQL Sub RunSql(sql as string) 'Catch Control Validator errors if not page.isvalid then response.write("Gespeicherte Prozedur wurde nicht ausgeführt") rebind exit sub end if try Dim mycommand2 As New OleDbCommand(sql,myConnection) myConnection.Open() myCommand2.ExecuteNonQuery() myConnection.Close() 'editing ausschalten
359
ASP.NET 360
16 Grid1.EditItemIndex = -1 Catch ex As OleDbException ' SQL error Dim errItem As OleDbError Dim errString As String For Each errItem In ex.Errors errString += ex.Message + "" Next Message.Text = "SQL Error.Details follow:" & errString Message.Style("color") = "red" Catch myException as Exception Response.Write("Exception: " + myException.ToString()) Message.Style("color") = "red" End try rebind response.write(sql) End Sub
Der folgende Listingsauszug enthält die modifizierte Version dieser Funktion: Listing 16.4: Modifizierte Version der RunSQL-Methode Sub RunSql(vSQL as string) 'Catch Control Validator errors if not page.isvalid then response.write("Gespeicherte Prozedur wurde nicht ausgeführt ") rebind exit sub end if Dim t As New NameSpaceHersh.SQLService Dim s As string s = t.RunSQL(ConnStr,vSQL) Grid1.EditItemIndex = -1 Rebind if s "Success" then Message.Text = s Message.Style("color") = "red" End if End Sub
Integration von Webservices 16 Diese Funktion ruft die RunSQL-Funktion des Webservices auf und übergibt ihr den Connection-String sowie den SQL-/Prozeduraufruf als Parameter. Wenn der Aufruf bzw. die Prozedur erfolgreich ausgeführt wird, wird der String Success von der Funktion zurückgegeben. Sonst wird der entsprechende Fehlerstring im Browser angezeigt. Hier ist nun der gesamte Code der Code-Behind-Datei Masters.vb, nachdem wir die beiden Methoden wie oben besprochen in der zweiten Fassung belassen. An dem Webformular Masters.aspx muss nichts weiter verändert werden. Listing 16.5: Masters3.vb Option Strict Off Imports System Imports System.Collections Imports System.Text Imports System.Data Imports System.Data.OleDb Imports System.Web.UI Imports System.Web.UI.WebControls Public Class BaseClass Inherits System.Web.UI.Page Protected Grid1 as DataGrid Protected Message as label Protected acode_category as dropdownlist Protected AddPanel as Panel Dim ConnStr As String Dim SQL As String Sub Page_Load(Source As Object, E As EventArgs) ConnStr = "Provider=SQLOLEDB; Data Source=(local); " ConnStr = ConnStr+" Initial Catalog=ASPNET;User ID=sa;" if NOT (isPostBack) rebind end if End Sub Sub ReBind() Dim t As New NameSpaceHersh.SQLService Dim ds As DataSet ' Bind Grid SQL = "select m.*, g.code_display as category "
361
ASP.NET 362
16 SQL = SQL + "from masters m, groups g " SQL = SQL + " where m.code_category = g.code_value" ds = t.Populate(ConnStr, SQL) Grid1.DataSource=ds.Tables("vTable").DefaultView Grid1.DataBind() 'populate drop down list SQL = "Select * from groups order by code_display" ds = t.Populate(ConnStr, SQL) acode_category.DataSource=ds.Tables("vTable").DefaultView acode_category.DataBind() hidePanel() End Sub Sub Grid1_Edit(Sender As Object, E As DataGridCommandEventArgs) Grid1.EditItemIndex = E.Item.ItemIndex ReBind() End Sub Sub Grid1_Cancel(Sender As Object, E As DataGridCommandEventArgs) Grid1.EditItemIndex = -1 ReBind() End Sub Sub hidePanel() if AddPanel.visible = true then AddPanel.visible = false end if End Sub Sub RunSql(vSQL as string) 'Catch Control Validator errors if not page.isvalid then response.write("Gespeicherte Prozedur wurde nicht ausgeführt") rebind exit sub end if Dim t As New NameSpaceHersh.SQLService Dim s As string s = t.RunSQL(ConnStr,vSQL) Grid1.EditItemIndex = -1 Rebind if s "Success" then Message.Text = s
Integration von Webservices 16 Message.Style("color") = "red" End if End Sub End Class
363
Einbinden eines Webservice in ein Transaktionsformular
ASP.NET 366
17 In diesem Kapitel geht es um ein Transaktionswebformular (Transactions.aspx zusammen mit dem Code Behind von Transactions.vb) für einen persönlichen Finanzbuchhaltungsmanager. In diesem Kapitel zeige ich ihnen, wie man dieses Formular für die Datenbankinteraktion mit und ohne dem Webservice SQLService benutzt. Ich werde dazu jeweils die beiden Methoden ReBind und RunSQL verändern, die beide in Transactions.vb zu finden sind. Das aspx-Formular bleibt jeweils unverändert. Die ReBind-Methode bindet ein DataGrid und ein DropDownList-Control an eine Datenbanktabelle. Hier ist die erste Version der Methode, die ich danach verändern werde: Listing 17.1: ReBind-Methode ohne SQLService-Webservice Sub ReBind() sql = " select m.code_value,m.code_display,t.*, h.* ," sql = sql + "(select code_display from masters where code_value = t.posted_to) " sql = sql + " as posted_display " sql = sql + " from tr_header h,transactions t, masters m " sql = sql + " where t.doc_no = h.doc_no " sql = sql + " and m.code_value = t.code_value" sql = sql + " and m.code_value = (select selection from tblSelection)" myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "transactions") 'Binding a Grid Grid1.DataSource=ds.Tables("transactions").DefaultView Grid1.DataBind() 'populate account selection drop down list ' which is visible in the add mode sql = "Select * from masters where code_value " sql = sql + " (select selection from tblSelection)" myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "masters")
Einbinden eines Webservice 17 aposted_display.DataSource=ds.Tables("masters").DefaultView aposted_display.DataBind() addshow.visible = true
Um den SQLService-Webservice zu benutzen, habe ich es so verändert: Listing 17.2: ReBind-Methode mit SQLService-Webservice Sub ReBind() Dim t As New NameSpaceHersh.SQLService Dim ds As DataSet SQL = " select m.code_value,m.code_display,t.*, h.* ," sql = sql + "(select code_display from masters where " sql = sql + " code_value = t.posted_to) as posted_display " sql = sql + " from tr_header h,transactions t, masters m " sql = sql + " where t.doc_no = h.doc_no " sql = sql + " and m.code_value = t.code_value" sql = sql + " and m.code_value = (select selection from tblSelection)" ds = t.Populate(ConnStr, SQL) Grid1.DataSource=ds.Tables("vTable").DefaultView Grid1.DataBind() 'populate account(add mode) selection drop down list 'SQL = "Select * from masters" SQL = "Select * from masters where code_value " SQL = SQL + " (select selection from tblSelection)" ds = t.Populate(ConnStr, SQL) aposted_display.DataSource=ds.Tables("vTable").DefaultView aposted_display.DataBind() addshow.visible = true End Sub
In dieser Funktion rufe ich die Webservice-Methode Populate zweimal auf. Jedes Mal übergebe ich dabei einen ConnectionString und einen SQL-Aufruf. Die Funktion gibt einen Datensatz zurück, der die Datenbankzeilen enthält. Diese benutze ich, um das DataGrid und das DropDownList-Control daran zu binden.
367
ASP.NET 368
17 Die zweite Funktion, die wir verändern müssen, ist RunSQL. Diese Funktion wird dazu verwendet, eine Zeile hinzuzufügen, zu löschen oder zu aktualisieren. Die entsprechenden Eventhandler übergeben dieser Funktion eine SQL Action Query oder einen gespeicherten Prozedurnamen mit den jeweiligen Parametern. Hier ist der VB-Auszug: Listing 17.3: RunSQL-Funktion ohne SQLService-Webservice Sub RunSql(vsql as string) 'Catch Control Validator errors if not page.isvalid then response.write("Gespeicherte Prozedur wurde nicht ausgeführt") rebind exit sub end if try Dim mycommand2 As New OleDbCommand(vsql,myConnection) myConnection.Open() myCommand2.ExecuteNonQuery() myConnection.Close() 'editing ausschalten Grid1.EditItemIndex = -1 Catch ex As OleDbException ' SQL error Dim errItem As OleDbError Dim errString As String For Each errItem In ex.Errors errString += ex.Message + "" Next Message.Style("color") = "red" Response.write("DataBase Error :" + errString) Catch myException as Exception Response.Write("Exception: " + myException.ToString()) Message.Style("color") = "red" Finally message.text = vsql end try End Sub Sub hidePanel() if AddPanel.visible = true then
Einbinden eines Webservice 17 AddPanel.visible = false 'reset values adate.text = "" aref.text = "" adr_amount.text = "" acr_amount.text = "" anarr.text = "" end if End Sub
Hier ist die modifizierte Version dieser Funktion: Listing 17.4: RunSQL-Funktion mit SQLService-Webservice Sub RunSql(vsql as string) if not page.isvalid then response.write("Gespeicherte Prozedur wurde nicht ausgeführt ") rebind exit sub end if Dim t As New NameSpaceHersh.SQLService Dim s As string s = t.RunSQL(ConnStr,vSQL) Grid1.EditItemIndex = -1 Rebind if s "Success" then Message.Text = s Message.Style("color") = "red" End if End Sub
Diese Funktion ruft die RunSQL-Funktion des Webservices auf und übergibt ihr den Connection-String sowie die SQL-Zeichenfolge als Parameter. Wenn der Aufruf bzw. die Prozedur erfolgreich ausgeführt wird, wird der String Success von der Funktion zurückgegeben. Sonst wird der entsprechende Fehlerstring im Browser angezeigt. Hier ist nun das Listing des Code-Behind-Formulars Transactions.vb
369
ASP.NET 370
17 Listing 17.5: Transactions.vb Option Strict Off Imports System Imports System.Collections Imports System.Text Imports System.Data Imports System.Data.OleDb Imports System.Web.UI Imports System.Web.UI.WebControls Public Class BaseClass Inherits System.Web.UI.Page Protected Grid1 as DataGrid Protected Message as label, title as label Protected aposted_display as dropdownlist, selection as dropdownlist Protected AddPanel as Panel Protected adate as TextBox, aref as TextBox, adr_amount as TextBox Protected acr_amount as TextBox , anarr as TextBox Protected addshow as button Dim ds As New DataSet Dim ConnStr As String Dim SQL As String Sub Page_Load(Source As Object, E As EventArgs) ConnStr = "Provider=SQLOLEDB; Data Source=(local); " ConnStr = ConnStr+" Initial Catalog=ASPNET;User ID=sa;" if NOT (isPostBack) Dim code As string, display As string code = Request.Form("Selection") title.text = code if code = "" then response.redirect("selection.aspx") end if UpdateSelection(code) rebind end if End Sub Sub ReBind() Dim t As New NameSpaceHersh.SQLService Dim ds As DataSet SQL = " select m.code_value,m.code_display,t.*, h.* ,"
Einbinden eines Webservice 17 sql = sql + "(select code_display from masters where " sql = sql + " code_value = t.posted_to) as posted_display" sql = sql + " from tr_header h,transactions t, masters m " sql = sql + " where t.doc_no = h.doc_no " sql = sql + " and m.code_value = t.code_value" sql = sql + " and m.code_value = (select selection from tblSelection)" ds = t.Populate(ConnStr, SQL) Grid1.DataSource=ds.Tables("vTable").DefaultView Grid1.DataBind() 'populate account(add mode) selection drop down list sql = "Select * from masters where code_value " sql = sql + " (select selection from tblSelection)" ds = t.Populate(ConnStr, SQL) aposted_display.DataSource=ds.Tables("vTable").DefaultView aposted_display.DataBind() addshow.visible = true End Sub Sub Grid1_Edit(Sender As Object, E As DataGridCommandEventArgs) Grid1.EditItemIndex = E.Item.ItemIndex ReBind() End Sub Sub Grid1_Cancel(Sender As Object, E As DataGridCommandEventArgs) Grid1.EditItemIndex = -1 ReBind() End Sub Sub RunSql(vsql as string) if not page.isvalid then response.write("Gespeicherte Prozedur wurde nicht ausgeführt ") rebind exit sub end if Dim t As New NameSpaceHersh.SQLService Dim s As string s = t.RunSQL(ConnStr,vSQL) Grid1.EditItemIndex = -1 Rebind if s "Success" then
371
ASP.NET 372
17 Message.Text = s Message.Style("color") = "red" End if End Sub Sub hidePanel() if AddPanel.visible = true then AddPanel.visible = false 'reset values adate.text = "" aref.text = "" adr_amount.text = "" acr_amount.text = "" anarr.text = "" end if End Sub Sub UpdateSelection(vselection) sql = "delete from tblSelection " sql = sql + " insert into tblSelection(selection)" sql = sql + " values('" + vselection + "')" runSql(sql) End Sub End Class
Integration des Webservice in einen Saldenbilanzbericht
Projekt 1 – Zusammenfassung
378
ASP.NET 374
18 In diesem Kapitel werde ich Ihnen das SaldenbilanzberichtWebformular TrialBalance.aspx vorstellen. Auch hier werden wir es uns einmal ohne und einmal mit dem SQLService-Service anschauen. Wir müssen dazu die ReBind-Methode modifizieren, die zwei DataGrid Controls an zwei DataView Controls bindet. Hier ist der Code der Funktion ohne SQLService: Listing 18.1: ReBind-Funktion ohne SQLService-Benutzung Sub ReBind() sql = "SELECT code_display, closing, " sql = sql + " dr_amount = CASE type WHEN 'A' THEN " sql = sql + " closing WHEN 'E' THEN closing ELSE 0 END, " sql = sql + " cr_amount = CASE type WHEN 'I' " sql = sql + " THEN closing WHEN 'L' THEN closing ELSE 0 END " sql = sql + " From Masters" myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "Masters") 'Binding a Grid Grid1.DataSource=ds.Tables("Masters").DefaultView Grid1.DataBind() 'totals sql = "SELECT 'Total' as nothing ," sql = sql + " (Select sum(closing) From masters " sql = sql + " where type in('A','E')) as dr_total , " sql = sql + " (Select sum(closing) From masters " sql = sql + " where type in('I','L')) as cr_total " myCommand = New OleDbDataAdapter(SQL, myConnection) myCommand.Fill(ds, "totals") 'Binding a Grid Grid2.DataSource=ds.Tables("totals").DefaultView Grid2.DataBind() End Sub
Um den SQLService-Service zu benutzen, müssen Sie diese Funktion wie folgt abändern:
Integration des Webservice 18 Listing 18.2: ReBind-Funktion mit SQLService-Benutzung Sub ReBind() Dim t As New NameSpaceHersh.SQLService Dim ds As DataSet sql = "SELECT code_display, closing, " sql = sql + " dr_amount = CASE type WHEN 'A' THEN " sql = sql + " closing WHEN 'E' THEN closing ELSE 0 END, " sql = sql + " cr_amount = CASE type WHEN 'I' " sql = sql + " THEN closing WHEN 'L' THEN closing ELSE 0 END " sql = sql + " From Masters" ds = t.Populate(ConnStr, SQL) Grid1.DataSource=ds.Tables("vTable").DefaultView Grid1.DataBind() 'totals sql = "SELECT 'Total' as nothing ," sql = sql + " (Select sum(closing) From masters " sql = sql + " where type in('A','E')) as dr_total , " sql = sql + " (Select sum(closing) From masters " sql = sql + " where type in('I','L')) as cr_total " ds = t.Populate(ConnStr, sql) Grid2.DataSource=ds.Tables("vTable").DefaultView Grid2.DataBind() End Sub
Die Webservice-Methode Populate wird zweimal aufgerufen. Jedes Mal wird ihr der Connection-String und der SQL-Aufruf übergeben. Ein Datensatz, der die Datenbankzeilen beinhaltet und zur Bindung der zwei DataGrid Schnittstellen verwendet wird, wird daraufhin zurückgegeben. Hier ist das vollständige Listing der TrialBalance.aspx mit Benutzung des SQLService-Services: Listing 18.3: TrialBalance.aspx mit SQLService-Benutzung
375
ASP.NET 376
18 Dim ConnStr As String Dim SQL As String Sub Page_Load(Source As Object, E As EventArgs) ConnStr = "Provider=SQLOLEDB; Data Source=(local); " ConnStr = ConnStr+" Initial Catalog=ASPNET;User ID=sa;" if NOT (isPostBack) rebind end if End Sub Sub ReBind() Dim t As New NameSpaceHersh.SQLService Dim ds As DataSet sql = "SELECT code_display, closing, " sql = sql + " dr_amount = CASE type WHEN 'A' THEN " sql = sql + " closing WHEN 'E' THEN closing ELSE 0 END, " sql = sql + " cr_amount = CASE type WHEN 'I' " sql = sql + " THEN closing WHEN 'L' THEN closing ELSE 0 END " sql = sql + " From Masters" ds = t.Populate(ConnStr, SQL) Grid1.DataSource=ds.Tables("vTable").DefaultView Grid1.DataBind() 'totals sql = "SELECT 'Total' as nothing ," sql = sql + " (Select sum(closing) From masters " sql = sql + " where type in('A','E')) as dr_total , " sql = sql + " (Select sum(closing) From masters " sql = sql + " where type in('I','L')) as cr_total " ds = t.Populate(ConnStr, sql) Grid2.DataSource=ds.Tables("vTable").DefaultView Grid2.DataBind() End Sub a { color:black; text-decoration:none;} a:hover { color:red; text-decoration:underline;}
Integration des Webservice 18 Trial Balance
377
ASP.NET 378
18
Projekt 1 – Zusammenfassung Finanzbuchhaltungsprogramme über Browser auszuführen hat viele Vorteile. Sie können dadurch Information über relevante Daten von jedem beliebigen Ort aus abrufen. Es ist dadurch nicht nötig, Netzwerkinfrastrukturen aufzubauen, nur um verschiedene Buchhaltungsmodule miteinander kommunizieren zu lassen. Der Nachteil einer browsergesteuerten Anwendung ist, dass man nicht den gewohnten Bedienkomfort einer aufwändigen Benutzerschnittstelle genießen kann, den
Integration des Webservice 18 man von normalen Desktop-Anwendungen her gewöhnt ist. Wenn Sie jedoch den Datenbankzugriff und die Manipulationsroutinen in einen Webservice integrieren, können Sie mit Sprachen wie Visual Basic.NET oder C# ein schönes Graphical User Interface aufbauen. Den Webservice benutzen Sie dann dazu, um mit der Datenbank über das Internet zu kommunizieren. In diesem Projekt habe ich Ihnen gezeigt, wie das aussehen kann. Wir haben einen persönlichen Finanzbuchhaltungsmanager entwickelt, der über einen Webservice auf eine Datenbank zugreifen kann. Da dieses Buch von ASP.NET handelt, haben wir diese Sprache auch als Entwicklungssprache verwendet. Sie hätten genauso gut auch Visual Basic.NET oder C# benutzen können, um die Benutzerschnittstelle zu entwickeln. Diese kann dann über den Webservice auf die Datenbank zugreifen.
379
Projekt 2: Visual Studio.NET
ASP.NET 382
Projekt 2 – Überblick Visual Studio beinhaltet eine Vielzahl an Entwicklungstools, um Web- und Windows-Anwendungen komfortabel in möglichst kurzer Zeit zu entwickeln. Es ist eine integrierte Entwicklungsumgebung, die es Ihnen ermöglicht, Anwendungen mit unterschiedlichen Programmiersprachen zu entwickeln. Sie können dazu Visual Basic, C#, ASP.NET usw. verwenden. Unabhängig davon, welche dieser Sprachen Sie bevorzugen, brauchen Sie nur eine Entwicklungsumgebung zu erlernen. Visual Studio erneuert und erweitert die Funktionalität von Visual InterDev und ermöglicht ein schnelles Zusammenbauen von Programmen mit Hilfe von Drag & Drop. Die IDE bietet auch hervorragende Debugging-Möglichkeiten. Das funktioniert bei den Sprachen, den Prozessen und sogar bei den gespeicherten Prozeduren. In diesem Teil, der insgesamt drei Kapitel umfasst, werden wir die wichtigsten Features des Visual Studio kennen lernen. In Kapitel 19 Datenbankinteraktion mit Visual Studio.NET werde ich Ihnen zunächst einen Überblick über die verschiedenen Assistenten, Tools und Komponenten von Visual Studio.NET geben. Dann werden Sie ein Datenbank-Webformular aufbauen, indem Sie die Drag & Drop-Features von Visual Studio intensiv nutzen. In Kapitel 20 CRUD-Applikationen mit Visual Studio.NET werden Sie CRUD (create-read-update-delete) in dieses Webformular implementieren. Am Ende des zweiten Projektes werden Sie mit Visual Studio.NET Webservices in Windeseile entwickeln können.
Datenbankinteraktion mit Visual Studio.NET
Erstellung der C#-Webanwendung
384
Der Anwendungsordner
386
Die erzeugten Dateien
386
Die Projekt-Eigenschaften (Project Properties)
388
Datenbankinteraktion
389
Erstellen einer Verbindung (Connection) und eines DataAdapters
390
Der generierte Code von SqlConnection und SqlDataAdapter
396
Das DataSet
400
ASP.NET 384
19 In diesem Kapitel werde ich Ihnen zeigen, wie man ein DataGrid an ein Strongly Typed DataSet« bindet. Und im weiteren Verlauf werde ich Ihnen die verschiedenen Tools, Assistenten und Komponenten von Visual Studio.NET für die Datenbankinteraktion vorstellen. Ein Strongly Typed DataSet ist ein von Visual Studio.Net erzeugtes DataSet. Der Entwickler schreibt keinen Code für dieses DataSet, sondern zieht einfach ein DataSet-Objekt aus der Visual Studio.NET-ToolBox. Ein so erzeugtes DataSet ist enger an das .NET Framework gebunden als ein von Hand geschriebenes DataSet. Das Typed DataSet profitiert von der Syntaxvervollständigung des Visual Studio.NET, welches zur produktiveren Entwicklung führen kann. Ein Typed DataSet ist eine Unterklasse der DataSet-Klasse und besitzt eine Schemadatei (eine .XSDDatei), welche die Struktur der Tabellen in dem DataSet beschreibt. Dieses Schema enthält die Tabelle mit den Spaltennamen, die Datentypen und die Informationen über die Beschränkungen (constraints) der Daten. Ein Untyped DataSet hat kein entsprechendes Schema. Visual Studio.NET erzeugt dieses Datenbankschema vollautomatisch. Anschließend werde ich Sie durch eine in C# geschriebene Webanwendung leiten, welche die Daten der authors-Tabelle der PUBS-Datenbank in einem DataGrid anzeigt.
Erstellung der C#-Webanwendung Visual Studio.NET organisiert die Entwicklung von Anwendungen nach dem Projektmappen- (Solutions) und Projekte-Konzept. Eine Projektmappe kann eine oder mehrere Projekte beinhalten und ein Projekt ist wiederrum ein Verzeichnis, das alle Objekte einer Anwendung zusammenhält. In diesem Schritt werde ich ein C#-ASP.NET-Webprojekt erstellen. Dies sind die dazu nötigen Schritte: Erstellen Sie eine Webanwendung durch klicken auf die Schaltfläche Neues Projekt auf der Startseite oder über das Menü Datei/Neu/Projekt. Wählen Sie, wie in Abbildung 19.1 gezeigt, Visual C#Projekte auf der linken und ASP.NET-Webanwendung auf rechten Seite. Sie können das Projekt VSÜberblick nennen.
Datenbankinteraktion mit Visual Studio.NET 19
385
Erstellung einer ASP.NETWebanwendung Abb. 19.1
VS erstellt einen virtuellen Ordner im IIS. Abb. 19.2
ASP.NET 386
19
Der Anwendungsordner VS erstellt einen Anwendungsordner mit dem Namen VSÜberblick im Verzeichnis wwwroot. Es wird auch einen virtuellen Ordner in IIS erstellen. Sie können sich das anschauen, indem Sie mit der rechten Maustaste auf Arbeitsplatz klicken und dann mit der linken Verwalten auswählen. Unter Dienste und Anwendungen/Internet-Informationsdienste/Websites/Standardwebsite werden Sie dann ein neues VSÜberblick-Verzeichnis entdecken können.
Die erzeugten Dateien Schauen Sie sich bitte jetzt die ASP.NET-Webanwendung an, die von VS (Visual Studio) erstellt wurde. Ihr Bildschirm sollte wie in Abbildung 19.3 aussehen.
Das von VS erzeugte leere Projekt Abb. 19.3
Auf der rechten oberen Seite des Bildschirms sehen Sie den Projektmappen-Explorer (Solution Explorer). Sie werden feststellen, dass VS eine Global.asax-Datei, eine Web.config-Datei und ein Standard- Webformular mit dem Namen WebForm1.aspx erstellt hat. Die Global.asax-Datei ist für die Logik der Anwendung durch Anwendungs-Events wie zum Bei-
Datenbankinteraktion mit Visual Studio.NET 19 spiel Application_Start, Application_End, Session_Start und Session_End zuständig. In Kapitel 10 ASP.NET-Anwendungen können Sie auch noch Näheres darüber in Erfahrung bringen. Die Web.config-Datei speichert die Konfigurationsdaten der Anwendung. Sie befindet sich im Root-Verzeichnis des Anwendungsverzeichnisses und ist auch aus allen Unterverzeichnissen heraus zugreifbar. In ihr werden Fehler-, Sicherheits-, Kompilations-, Debugging-, Session- und Globalinformationen gespeichert. Auch dazu finden Sie in Kapitel 10 ASP.NET-Anwendungen Genaueres. Das Webformular WebForm1.aspx wird als Vorlage zur Verfügung gestellt. Sie können daraus ein neues Webformular erstellen. Schauen Sie sich bitte die beiden Schaltflächen Entwurf (Design) und HTML unter dem Arbeitsbereich an. Beim Start von VS.NET befinden Sie sich automatisch im Modus Entwurf (Design). Der Modus Entwurf (Design) erlaubt Ihnen durch einfaches Drag & Drop, die Komponenten auf das Formular zu ziehen. Dort können Sie sie auch mit der Maus nach Belieben visuell anordnen. Webseiten, die durch VS erstellt werden, benutzen Code-Behind-Dateien. Wenn Sie ins VSÜberblick-Verzeichnis in Ihrem wwwroot-Ordner hineinschauen, können Sie die Code-BehindDatei WebForm1.aspx.cs sehen. Um sich die Datei in VS anzuschauen, haben sie dazu eine der folgenden Möglichkeiten: Wählen Sie aus dem Projekt-Menü Alle Dateien anzeigen (Show All Files). Dadurch taucht ein Plus-Zeichen vor WebForm1.aspx im Projektmappen-Explorer (Solution) auf. Wenn Sie auf das Pluszeichen klicken, werden Sie die Code-Behind-Datei sehen können. Im Projektmappen-Explorer (Solution) gibt es einen Button, der Alle Dateien anzeigen (Show All Files) als Hinweistext anzeigt, sobald Sie mit dem Mauszeiger darüberfahren. Klicken Sie ihn an. Rechtsklicken Sie auf WebForm1.aspx und wählen Sie Zeige Code (View Code). Beachten Sie, dass im Code Behind bereits einige Namensräume (namespaces) importiert sind. In der Entwicklungsphase werden Sie vielleicht viele verschiedene Webformulare erstellen. Wenn Sie sich eines davon im
387
ASP.NET 388
19 Browser anschauen wollen, klicken Sie mit der rechten Maustaste auf den Namen des Formulars und wählen Sie In Browser anzeigen (View in Browser). Sie können auch ein Webformular als Startformular auswählen. Wenn Sie Ihre Anwendung ausführen, wird im Browser dieses Formular als erstes angezeigt. Klicken Sie dazu mit der rechten Maustaste auf das Webformular und wählen Sie anschließend Als Startseite festlegen (Set as Start Page) aus.
Die Projekt-Eigenschaften (Project Properties) Rechtsklicken Sie bitte auf den Projektnamen im Projektmappen-Explorer und wählen Sie Eigenschaften (Properties). Das Eigenschaften-Fenster öffnet sich, wie in Abbildung 19.4 gezeigt.
Die ProjektEigenschaftenseiten Abb. 19.4
Sie können den Namensraum (namespace) Ihres Projektes unter Standardnamespace eintragen. Die Vorgabe ist Ihr Projektname. Sollten Sie hier den Namespace ändern, wird dies nicht automatisch in den schon erstellten Formularen übernommen. Sie müssten dann die Namespaces der einzelnen Formulare von Hand ändern, sonst gibt es Kompilierfehler. Jedes neu
Datenbankinteraktion mit Visual Studio.NET 19 erstellte Webformular wird jedoch in dem hier vorgegebenen Namensraum erzeugt. Jetzt wollen wir der WebForm1.aspx-Datei einen bedeutungsvolleren Namen geben. Rechtsklicken Sie bitte diese Datei im Projektmappen- Explorer und wählen Sie Umbenennen (Rename). Nennen Sie sie bitte TypedDataSet.aspx. Wenn Sie sich jetzt den Code Behind genauer anschauen, werden Sie feststellen, dass der Klassenname und der Konstruktorname immer noch WebForm1 heißen. Falls Sie hier die Namen nicht zu TypedDataSet umändern, werden Sie später Erstellungsfehler bekommen, wenn Sie dem Formular neue Komponenten hinzufügen. Das ist natürlich leicht zu beheben, indem Sie einfach den Klassen- und Konstrukturnamen von WebForm1 in TypedDataSet umbenennen. Sie können das entweder über Finden und Ersetzen bewerkstelligen oder Sie löschen einfach die Datei und erstellen Sie erneut mit dem neuen Namen. Ein Konstruktur ist eine Methode, die den gleichen Namen hat wie die Klasse. Das ist die erste Methode, die ausgeführt wird, sobald die Klasse instanziiert wird. (Ein Webformular ist eine Klasse.) Ein leerer Konstruktor, der keine Parameter besitzt, wird von Haus aus mit einer Klasse kreiert. Natürlich können Sie zusätzliche Konstruktoren hinzufügen, die Parameter akzeptieren.
Datenbankinteraktion In diesem Abschnitt werde ich Ihnen zeigen, wie man auf eine Datenbank zugreift, einen DataAdapter bevölkert (populate) und ihn dazu benutzt, einen Datensatz (DataSet) zu füllen. Dazu werde ich die authors-Tabelle der PUBS-Datenbank zu Demonstrationszwecken benutzen. Ich werde Ihnen zusätzlich noch zwei alternative Methoden zur Datenbankinteraktion zeigen. Alle Komponenten, die mit einer Datenbank interagieren, findet man in der Daten-Sektion der Toolbox. Um eine dieser Komponenten zu benutzen, müssen Sie sie nur anklicken und auf das Webformular ziehen. Abbildung 19.5 zeigt Ihnen die Komponenten, die für die Datenbankinteraktion zur Verfügung stehen.
389
ASP.NET 390
19
Die Data-Sektion der Toolbox Abb. 19.5
Erstellen einer Verbindung (Connection) und eines DataAdapters Ich werde den SQL Managed Provider verwenden, um eine Verbindung mit der Datenbank zu erstellen. Wie Sie sich vielleicht erinnern, kann für eine Datenbankinteraktion eine SQLConnection verwendet werden. Ein SQL-Select-Befehl nutzt diese Verbindung, um den SqlDataAdapter mit Datenbankdaten zu füllen. Wie versprochen werde ich Ihnen zwei unterschiedliche Methoden zeigen, mit denen Sie das erreichen können. Erstellen einer SqlConnection und eines SqlDataAdapters – Methode 1
Um eine SQLConnection und einen SqlDataAdapter zu erstellen, ziehen Sie einen SqlDataAdapter aus der Daten-Sektion der ToolBox auf das Webformular. Anschließend steht Ihnen ein Assistent zur Seite. Und so wird es gemacht: 1
Ziehen Sie einen SqlDataAdapter aus der Daten-Sektion der ToolBox auf das Webformular. Ein Datenadapter-Konfigurations-Assistent (Data Adapter Configuration Wizard) erscheint und hilft Ihnen, eine Datenbankverbindung herzustellen und die entspre-
Datenbankinteraktion mit Visual Studio.NET 19
391
chenden Kommandos zu erzeugen. Klicken Sie auf Weiter (Next) und das Fenster Wählen sie Ihre Datenverbindung (Choose your DataConnection) erscheint. Klicken sie auf Neue Verbindung (New Connection) und Sie sehen die Datenverknüpfungseigenschaftenseite (Data Link Properties). 2
Dieses Fenster sollte wie in Abbildung 19.6 gezeigt ausgefüllt werden. Hier sind einige Details: Bestimmen Sie den Servernamen, den Benutzernamen und das Kennwort. Wenn Sie die pubs-Datenbank im Ausgangszustand belassen, ist der Benutzername sa und das Kennwort leer. Selektieren Sie das Kontrollkästchen Speichern des Kennworts zulassen (Allow saving of password). Schließlich wählen Sie noch die pubs-Datenbank unter Wählen Sie die Datenbank auf dem Server aus (Select database on server) und klicken auf die Schaltfläche Verbindung testen (Test Connection), um die Verbindung zu testen.
Das Datenverknüpfungseigenschaften-Fenster Abb. 19.6
3
Im folgenden Schritt präsentiert Ihnen der Assistent drei Abfragetypoptionen: SQL Anweisungen verwenden (Use SQL statements), Neue gespeicherte Prozedu-
ASP.NET 392
19 ren erstellen (Create new stored procedures) und Vorhandene gespeicherte Prozeduren verwenden (Use existing stored procedures). Das ist in Abbildung 19.7 zu sehen. Wie ich in Kürze beschreiben werde, kann ein insert-, update-, delete- und select-Befehl mit dem DataAdapter assoziiert werden. Die Option SQL-Anweisungen verwenden (Use SQL statements) erstellt vier SQL-Befehle für die Zeilen der Datenbank, und zwar jeweils eine für select, insert, update und delete. Die Option Neue gespeicherte Prozeduren erstellen (Create new stored procedures) erstellt vier neue gespeicherte Prozeduren und die Option Vorhandene gespeicherte Prozeduren (Use existing stored procedures) ermöglicht Ihnen die Namen von vier bereits erstellten gespeicherten Prozeduren anzugeben. Für unsere Demonstration wählen Sie bitte die Option SQL-Anweisungen verwenden (Use SQL statements).
Das Fenster Abfragetyp auswählen (Choose a query) Abb. 19.7
Ein neues Fenster mit dem Namen SQL-Anweisungen generieren (Generate the SQL Statement) erscheint. Dieses Fenster erlaubt Ihnen einen Select-Befehl zu spezifizieren. Schreiben Sie Select * from authors, wie in Abbildung 19.8 gezeigt.
Datenbankinteraktion mit Visual Studio.NET 19
393
Ein SQL Befehl im Fenster Erzeugen Sie einen SQLBefehl (Generate the SQL Statement) Abb. 19.8
Sie können in diesem Fenster einen SQL-Befehl auch graphisch erzeugen. Dazu klicken Sie bitte auf die Schaltfläche AbfrageGenerator (Query Builder). Dadurch wird ein Abfrage-Generator-Tool (Query Builder) zum Vorschein gebracht. Sehen Sie sich bitte dazu die Abbildung 19.9 an. Dieses Tool ist ähnlich aufgebaut wie Microsoft Access. Hier können Sie mehrere Tabellen selektieren und verknüpfen, um anschließend durch Drag & Drop der Spaltennamen die Befehle zu generieren. Die weiteren Einzelheiten zu erkunden überlasse ich aber Ihnen. Nachdem Sie mit Ihrem SQL-Befehl zufrieden sind, drücken Sie bitte die Schaltfläche Weiter und anschließend die Schaltfläche Finish. Ein SqlDataAdapter und eine SqlConnection werden daraufhin im unteren Teil Ihres Webformulars erscheinen (siehe Abbildung 19.10). Diese werden SqlDataAdapter1 und SqlConnection1 heißen. Das ist das Ergebnis, das wir durch die erste Methode erreichen wollten. Wir haben mit VS einen SqlDataAdapter und eine SqlConnection kreiert. Jetzt widmen wir uns Methode 2.
ASP.NET 394
19
Der Abfrage-Generator erlaubt Ihnen die Abfragen graphisch zu erstellen. Abb. 19.9
Der SqlDataAdapter1 und die SqlConnection1 wurden in VS erzeugt. Abb. 19.10
Erstellen einer SqlConnection und eines SqlDataAdapters – Methode 2
Jetzt werde ich Ihnen eine andere und schnellere Methode zeigen, wie man eine SqlConnection und einen SqlDataAdapter erstellt. Sie sollten dafür am besten von vorne beginnen. Also erstellen Sie bitte zunächst ein neues Webformular mit dem
Datenbankinteraktion mit Visual Studio.NET 19
395
Namen TypedDataSet2.aspx. Da Sie dieses Formular im Browser anschauen wollen, setzen Sie es am besten als Startformular fest. Dazu klicken Sie bitte im Solution Explorer mit der rechten Maustaste auf das Formular und wählen Als Startseite festlegen (Set as Start Page) aus. Der Server-Explorer
Der Server-Explorer ist ein Datenbank-Explorer, der die verschiedenen Datenbankobjekte wie Tabellen, Trigger und gespeicherte Prozeduren anzeigt. Sie können ihn durch Ansicht/Server-Explorer (View/Server Explorer) im Menü oder das Tastaturkürzel S+A+s erreichen. Der Server-Explorer ist in Abbildung 19.11 gezeigt. Sie können ihn dazu verwenden, Ihre Tabellen zu manipulieren. Die weiteren Details überlasse ich Ihrem persönlichen Forschungsdrang.
Der Server-Explorer erlaubt Ihnen Datenbankobjekte anzuschauen und ihre Tabellen zu manipulieren Abb. 19.11
Alles, was Sie jetzt tun müssen, um eine SqlConnection und einen SqlDataAdapter zu erschaffen, ist die authors-Tabelle vom Server-Explorer auf das Formular zu ziehen. (Beziehen Sie sich auf die Abbildung 19.11, um diese Tabelle im Server-Explorer zu finden.) Das resultierende Formular sollte genauso aussehen wie in Abbildung 19.10. Es hat auch jeweils eine SqlConnection und einen SqlDataAdapter mit den Namen SqlConnection1 und SqlDataAdapter1.
ASP.NET 396
19
Der generierte Code von SqlConnection und SqlDataAdapter Wenn Sie auf SqlConnection1 rechtsklicken und Eigenschaften (Properties) auswählen, öffnet sich die Eigenschaftenseite, wie in Abbildung 19.12 gezeigt. Beachten Sie, dass Sie Name und ConnectionString ändern können. Außerdem ermöglicht Ihnen dieses Tool auch, die Eigenschaftseinstellungen durch andere Eigenschaftsseiten wie zum Beispiel Visual Basic zu verändern.
Die Eigenschaftsseite von SqlConnection1 Abb. 19.12
Die Eigenschaftsseite von SqlDataAdapter1 sieht entsprechend aus, wie Sie in Abbildung 19.13 sehen können. Schauen wir uns jetzt den erzeugten Code genauer an. Rufen Sie die Code-Behind-Datei in VS auf und erweitern Sie alle Knoten, indem Sie auf die Pluszeichen klicken. Dadurch wird jedes Plus zum Minus und Sie können den Code der entsprechenden Methode sehen. Beachten Sie bitte die Markierungen #region und #endregion. Code, der in diesen Tags eingeschlossen ist, kann aus- und eingeklappt werden. Natürlich können Sie auch in das Anwendungsverzeichnis wechseln und die Datei TypedDataSt.aspx.cs in einem beliebigen Texteditor betrachten.
Datenbankinteraktion mit Visual Studio.NET 19
397
Die Eigenschaftsseite von SqlDataAdapter Abb. 19.13
Mehrere Namensräume sind in der Code-Behind Datei-folgendermaßen importiert: using using using using using using using using using using
Eine Klasse mit dem Namen TypedDataSet, die von System.Web.UI.Page erbt, wird im VSÜberblick-Namespace folgendermaßen erzeugt: namespace VSÜberblick { public class TypedDataSet2 : System.Web.UI.Page { //Hier steht jede Menge Code } }
ASP.NET 398
19 Vier Command-Objekte werden deklariert: protected System.Data.SqlClient.SqlCommand sqlSelectCommand1; protected System.Data.SqlClient.SqlCommand sqlInsertCommand1; protected System.Data.SqlClient.SqlCommand sqlUpdateCommand1; protected System.Data.SqlClient.SqlCommand sqlDeleteCommand1; Eine SqlConnection wird deklariert: protected System.Data.SqlClient.SqlConnection sqlConnection1; Ein SqlDataAdapter wird deklariert: protected System.Data.SqlClient.SqlDataAdapter sqlDataAdapter1; Eine Instanz all dieser Objekte wird durch das Schlüsselwort new in der InitializeComponent-Methode fogendermaßen erzeugt: this.sqlSelectCommand1 = new System.Data.SqlClient.SqlCommand(); this.sqlInsertCommand1 = new System.Data.SqlClient.SqlCommand(); this.sqlUpdateCommand1 = new System.Data.SqlClient.SqlCommand(); this.sqlDeleteCommand1 = new System.Data.SqlClient.SqlCommand(); this.sqlConnection1 = new System.Data.SqlClient.SqlConnection(); this.sqlDataAdapter1 = new System.Data.SqlClient.SqlDataAdapter(); In derselben Methode wird SqlConnection wie folgt festgelegt: // sqlConnection1 this.sqlConnection1.ConnectionString = "data source=BHASIN\\NETSDK;initial catalog=pubs;integrated security=SSPI;persist security info=True;workstation id=BHASIN;packet size=4096";
Datenbankinteraktion mit Visual Studio.NET 19 Der DataAdapter wird mit einem Command-Objekt select, insert, update und delete folgendermaßen erzeugt: this.sqlDataAdapter1.DeleteCommand this.sqlDeleteCommand1; this.sqlDataAdapter1.InsertCommand this.sqlInsertCommand1; this.sqlDataAdapter1.SelectCommand this.sqlSelectCommand1; this.sqlDataAdapter1.UpdateCommand this.sqlUpdateCommand1;
= = = =
Nun werden für jedes Command-Objekt die folgenden Eigenschaften gesetzt: Die CommandText property Die Connection property Die Parameter Collection Obwohl es hier eine Menge Code gibt, ist er doch recht einfach zu verstehen. Zum größten Teil geht es hier um das Setzen der Parameter-Collection. Jedes Feld der Datenbanktabelle wird als Parameter der Parameter-Collection des Command-Objektes hinzugefügt. Dann wird entweder ein insert-, update- oder delete-Befehl erstellt, der all diese Felder beinhaltet. Diese Technik habe ich Ihnen in Kapitel 3 Verwendung von ADO.NET im .NET-Framework im Zusammenhang mit dem Aufruf von gespeicherten Prozeduren mit einem Command-Objekt genauer erklärt. Vielleicht rufen Sie sich dieses Kapitel noch einmal in Ihr Gedächtnis. Es ist jetzt angebracht, über diese vier Commands zu sprechen und auch den Datensatz ins Bild zu rücken. Wenn Sie in ADO 2.x select, insert, update oder delete in einer einzigen Methode unterbringen wollten, mussten Sie vier ADODB-Command-Objekte erzeugen. In ADO.NET gibt es zwar auch vier Commands, jedoch sind alle in einem DataAdapter- Objekt verlinkt, der alle vier Commands angemessen behandelt. Ich wollte das DataSet eigentlich im nächsten Abschnitt vorstellen, allerdings empfinde ich es im Augenblick angebracht, darüber zu sprechen. Wie Sie sich vielleicht erinnern, sitzt der DataAdapter zwischen dem DataSet und der Datenbank. Das DataSet kann auch an ein List-Control wie das DataGrid gebunden sein. Der Benutzer interagiert mit dem DataGrid und kann
399
ASP.NET 400
19 dadurch Datenbankdaten verändern. Da das DataSet an das DataGrid gebunden ist, werden alle Veränderungen am DataGrid auch am DataSet ausgeführt. Sie könnten auch eine Speichern-Schaltfläche auf dem DataGrid anbringen, damit ein Benutzer die Änderungen durch einen Klick darauf in der Datenbank speichern kann. Diese Änderungen könnten inserts, updates oder deletes auf Datenbankzeilen sein. Diese Schaltfläche würde die update()-Methode des DataAdapters aufrufen. Die update() Methode sucht im DataSet nach allen inserts, updates und deletes und ruft den entsprechenden insert-, updateoder delete-Befehl auf. Lassen Sie sich nicht durch den Methodennamen update() verirren, der DataAdapter führt gleichermaßen auch insert- und delete-Aktivitäten aus. In den TableMapping-Eigenschaften können Sie auch freundliche Namen (friendly names) angeben, um sich auf die Datenbank Spalten zu beziehen. Hier sind die Namen und die freundlichen Namen allerdings die gleichen. Zum Beispiel ist Folgendes das TableMapping für au_id. new System.Data.Common.DataColumnMapping("au_id", "au_id").
Diese Eigenschaften (properties) verlinken die Datenbankspalten des Datensatzes mit der Datenbank. Sollten Sie also sehr lange Namen in den Datenbankspalten haben, könnten Sie so mit einem kurzen Alias in dem DataTable darauf verweisen.
Das DataSet Im nächsten Schritt werde ich ein Strongly Typed DataSet erstellen, indem ich die Drag & Drop-Fähigkeiten von Visual Studio.NET ausnutze. Ich werde auch ein wenig Code zur Bevölkerung (populate) des DataAdapters und des DataSet hinzufügen und dann ein DataGrid an den Datensatz binden. Hier sind die einzelnen notwendigen Schritte: 1
Wählen Sie die Menüoption DataSet generieren (Generate Dataset) aus dem Menü Daten (Data).
2
Die Dialogbox DataSet generieren (Generate Dataset) erscheint, wie in Abbildung 19.14 zu sehen ist. Rufen Sie das DataSet dsAuthors auf und vergewissern Sie sich, dass das Kästchen mit der Beschriftung DataSet
Datenbankinteraktion mit Visual Studio.NET 19
401
zum Designer hinzufügen (Add this DatSet to the designer) angehakt ist.
Die Dialogbox DataSet generieren (Generate Dataset) Abb. 19.14
3
Eine Instanz des Datensatzes mit dem Namen dsAuthors1 erscheint nun im unteren Bereich des Formulars und das Objekt dsAuthors.xsd wird im Projektmappen-Explorer (Solution) angezeigt. Siehe dazu Abbildung 19.15. Wenn Sie sich den generierten Code in der Code-Behind-Datei anschauen, werden Sie Folgendes feststellen: Eine protected-Variable dsAuthors1 des Typs dsAuthors wurde deklariert: protected VSÜberblick.dsAuthors dsAuthors1;
Die DataSetName-, Locale- und Namespace-Eigenschaften für dsAuthors sind wie folgt gesetzt: // dsAuthors1 this.dsAuthors1.DataSetName = "dsAuthors"; this.dsAuthors1.Locale = new System.Globalization.CultureInfo("en-US"); this.dsAuthors1.Namespace = "http://www.tempuri.org/dsAuthors.xsd";
ASP.NET 402
19
Eine Instanz des Datensatzes wird erzeugt Abb. 19.15
4
Ziehen Sie nun ein DataGrid aus der Web Form Toolbox auf das Formular. Im nächsten Schritt werde ich dieses DataGrid an das DataSet binden.
5
Erzeugen Sie die Bind()-Methode, welche das DataSet mit der Fill()-Methode des DataAdapters populieren (populate) und das DataGrid an den Datensatz folgendermaßen binden wird: public void Bind() { sqlDataAdapter1.Fill(dsAuthors1); DataGrid1.DataSource=dsAuthors1; }
Letztendlich rufen Sie die Page_load-Event wie folgt auf:
Visual Studio.NET bietet Assistenten (Wizards) zur Erstellung von Methoden und Eigenschaften (Properties), allerdings überlasse ich es Ihnen, Genaueres darüber in Erfahrung zu bringen. Wenn Sie das Tasturkürzel S+H+cdrücken oder Ansicht/Klassenansicht (View/Class View) in dem Menü selektieren, erscheint das Fenster Klassenansicht. Diese Ansicht erlaubt Ihnen die Methoden, die Eigenschaften (Properties) und die Felder der Klasse zu erforschen. Sollten Sie auf den Klassennamen rechtsklicken und Hinzufügen (Add) wählen, werden Sie mit neuen Assistenten konfrontiert, die Ihnen helfen wollen, neue Methoden, Eigenschaften, Indizes oder Felder zu kreieren. 6
Führen Sie das Formular aus, indem Sie % drücken. Hier ist ein Screenshot der resultierenden Ausgabe:
Die resultierende Ausgabe Abb. 19.16
Sie werden es zu schätzen wissen, dass Visual Studio.NET es Ihnen erlaubt, Datenbankdaten in einem DataGrid mit nur einigen Zeilen Code zu präsentieren. Als RAD-Tool eignet sich Visual Studio.NET hervorragend. Der generierte Code ist elegant und präzise. Es lohnt sich, mit VS zu arbeiten, da sich die Entwicklungszeit dadurch beträchtlich senken lässt.
CRUD-Applikationen mit Visual Studio.NET
Erstellen Sie eine neue C#-ASP.NETWebanwendung
406
Die Datenkomponenten
408
DataGrid
409
Das Add-Panel
418
Die Command-Methoden des DataGrid spezifizieren
419
Methoden
420
ASP.NET 406
20 CRUD ist ein Akronym für Create-Read-Update-Delete, welche die vier Grundfunktionen einer Datenbankinteraktion darstellen. Visual Studio.NET kann jedoch noch mehr, als nur gut aussehende Oberflächen und entzückende Benutzerschnittstellen zu bauen. Wie Sie in diesem Kapitel sehen werden, kann Visual Studio.NET auch aufwändige Datenbankanwendungen erstellen, welche die Fähigkeit haben, select-, insert-, update- und delete-Befehle auf Datenbankdaten auszuführen. In diesem Kapitel werden wir ein Webformular erstellen, das Aufzeichnungen der stock_master-Tabelle zeigen kann. Mit Visual Studio.NET werden wir ein Formular erzeugen, das Datenbankdaten hinzufügen, modifizieren und löschen kann.
Erstellen Sie eine neue C#-ASP.NETWebanwendung Wie immer müssen Sie zunächst eine neue ASP.NET-Webanwendung erschaffen. Als Scriptsprache habe ich C# ausgewählt, und hier sind die dafür nötigen Schritte: 1
Erstellen Sie eine neue Webanwendung durch Klicken auf den Button Neues Projekt (New Project) auf der Startseite oder durch das Menü Datei/Neu/Projekt (File/New/Project).
2
Wählen Sie Visual C# Project auf der linken und ASP.NET-Webanwendung auf der rechten Seite aus. Ich habe dieses Projekt Kapitel20 genannt. Löschen Sie die Datei WebForm1.aspx, die automatisch erstellt wurde. Fügen Sie eine neue Web Form mit dem Namen StockMaster.aspx ein.
3
Wählen Sie eine Hintergrundfarbe für StockMaster .aspx. Klicken Sie dazu mit der rechten Maustaste auf einen leeren Bereich des Webformulars und wählen Sie Eigenschaften. Klicken Sie auf die Schaltfläche Hintergrund (Background color) aus der Registerkarte Farbe und Ränder (Color and Margins) der DOCUMENTEigenschaftenseiten (Document Property Page). Vergleichen Sie mit Abbildung 20.1. Ich habe mich für die Farbe #99cc99 entschieden.
CRUD-Applikationen mit Visual Studio.NET 20
407
Die Hintergrundfarbe des Webformulars Abb. 20.1
4
Fügen Sie den SqlClient-Namespace in die CodeBehind-Datei ein. Visual Studio.NET erstellt alle Namensräume bis auf den Sql Managed Provider automatisch. Erstellen Sie diesen Namensraum wie folgt: using System.Data.SqlClient;
Die Namespace-Sektion sieht nun so aus: using using using using using using using using using using using
Die Datenkomponenten Erstellen Sie jetzt eine SqlConnection, einen SqlDataAdapter und ein DataSet: 1
Erstellen Sie die SqlConnection und den SqlDataAdapter durch ziehen der stock_master-Tabelle der ASPNET-Sample- Datenbank vom Server-Explorer auf das Formular. Der Server-Explorer zeigt die stock_masterTabelle wie in Abbildung 20.2 angezeigt an.
Die stock_master-Tabelle im Server-Explorer Abb. 20.2
Eine SqlConnection und ein SqlDataAdapter mit den Namen SqlConnection1 und SqlDataAdapter1 werden entsprechend erstellt. Vergleichen Sie dazu Abbildung 20.3. SqlConnection und SqlDataAdapter wurden durch Visual Studio erzeugt. Abb. 20.3
CRUD-Applikationen mit Visual Studio.NET 20 2
Wählen Sie die Menüoption DataSet generieren (Generate DataSet) aus dem Menü Daten aus. Dazu müssen Sie im Entwurfsmodus (Design) sein. Sie können auch DataSet generieren (Generate DataSet) auf der Eigenschaftsseite des SqlDataAdapters auswählen. Die Dialogbox DataSet generieren (Generate DataSet) erscheint, wie in Abbildung 20.4 zu sehen ist. Rufen Sie den Datensatz dsStock auf und stellen Sie sicher, dass das Kontrollkästchen DataSet zu Designer hinzufügen (Add this DataSet to the designer) angewählt ist.
DataSet generieren Abb. 20.4
Eine Instanz des DataSet dsStock1 erscheint unten auf dem Formular. Das Objekt dsStock.xsd wird auch im Projektmappen-Explorer angezeigt.
DataGrid Als Hauptbenutzerschnittstelle dient das DataGrid. Um es hinzuzufügen, ziehen Sie ein DataGrid von der Web Forms-Toolbox auf das Webformular.
409
ASP.NET 410
20 Sie müssen nun einige Attribute für das DataGrid festlegen. Dazu klicken Sie bitte mit der rechten Maustaste auf das DataGrid und wählen Eigenschaften (Properties). Die Eigenschaftenseite (Property Page) sollte wie in Abbildung 20.5 aussehen.
Setzten der Attribute von DataGrid Abb. 20.5
Verschönern Sie das DataGrid, indem Sie auf den Hyperlink Autom. Formatierung am unteren Ende der Eigenschaftsseite klicken und Klassisch 2 auswählen. Nun setzen Sie bitte die DataGrid-Eigenschaften wie folgt: DataSource = dsStock1 DataMember = stock_master DataKeyField = code_value Ich habe sie in Abbildung 20.5 eingekreist. Wechseln sie nun in die HTML-Ansicht und beachten Sie, dass DataSource-, DataMembers- und DataKeyField-Attribute zum DataGrid-Elementtag hinzugefügt wurden: DataGrid-Spalten selektieren
Wenn Sie die Eigenschaftsseite (Property Page) nicht bereits geöffnet haben, tun Sie dies bitte, indem Sie auf das DataGrid rechtsklicken und Eigenschaften (Properties) auswählen. Klicken sie dann auf den Hyperlink Eigenschaftengenerator (Property Builder), der sich unten befindet. Klicken Sie auf Spalten (Columns). Selektieren Sie alle Spalten, indem Sie auf Alle Felder (AllFields) in der Listbox Verfügbare Spalten (Available Columns) und anschließend auf den Pfeil (>) klicken. Vergleichen Sie bitte mit Abbildung 20.6.
Die Spalten von DataGrid selektieren Abb. 20.6
Wenn Sie sich das Formular in der HTML-Ansicht anschauen, werden Sie sicherlich feststellen, dass das -Elementtag auf diese Weise generiert wurde:
Setzen Sie die closing-Spalte auf read_only, da diese Spalte automatisch durch einen Trigger in der stock_detail-Tabelle aktualisiert wird. Die Primärschlüsselspalte code_value sollte auch auf read_only gesetzt werden. Um dies zu erreichen, tippen Sie einfach in der HTML-Ansicht drauflos und Visual Studio.NET wird Ihnen durch die Auto-Complete-Funktion einige Tags in einer Drop-Down-Liste zur Verfügung stellen. Die closing- und code_value-Spalten sollten wie folgt aussehen: Erstellen Sie die Hyperlinks Hinzufügen, Bearbeiten und Löschen
Erstellen Sie jetzt bitte die Button-Spalten Bearbeiten, Aktualisieren, Abbrechen (die Hyperlinks Update und Cancel erscheinen im Update-Modus des DataGrid). Scrollen Sie dafür in der Box Verfügbare Spalten (Available Columns), bis Sie die Beschriftung Schaltflächenspalten (Button Columns) sehen. Wenn Sie die Liste erweitern, wird die Option Bearbeiten, Aktualisieren, Abbrechen wie in Abbildung 20.7 zu sehen zum Vorschein gebracht.
CRUD-Applikationen mit Visual Studio.NET 20
413
Die Edit-, Update- und Cancel-Buttons Abb. 20.7
Klicken Sie bitte auf die Schaltfläche Hinzufügen (>). Das wird einen Hyperlink Bearbeiten erzeugen. Wenn Sie sich das Webformular in der HTML-Ansicht anschauen, werden Sie sehen, dass die EditCommandColumn-Tags mit den UpdateText-, CancelText- und EditText- Attributen kreiert wurden. Vielleicht erinnern Sie sich noch daran, dass dies die Hyperlinküberschriften sind, die mit den entsprechenden Hyperlinks Bearbeiten, Aktualisieren und Abbrechen angezeigt werden. Anfangs wird der Bearbeiten-Hyperlink im DataGrid dargestellt. Wenn Sie darauf klicken, wird das DataGrid im Bearbeiten-Modus angezeigt und die Aktualisieren- und Abbrechen-Hyperlinks erscheinen dann in dieser Phase.
Erstellen Sie eine Delete-Schaltfläche auf die gleiche Art, wie Sie die Button-Spalten Edit, Update und Cancel erzeugt haben. Diese Button-Spalten-Auswahl befindet sich direkt unter der Auswahl für die Button-Spalten Edit, Update und Cancel in der Liste Verfügbare Spalten (Available Columns). So wird die Button-Spalte Delete in der HTML-Ansicht aussehen:
ASP.NET 414
20
Nun haben Sie in dem DataGrid zwei Hyperlinks mit den Namen Edit und Delete. Wenn sie sich nicht direkt nebeneinander befinden, können Sie sie mit den Pfeiltasten bewegen. Sie können sie auch entfernen, indem Sie das Kreuz benutzen. Ziehen Sie ein LinkButton-Control aus der ToolBox auf das Webformular. Platzieren Sie es bitte auf dem DataGrid unter der Seitenüberschrift. Rechtsklicken Sie bitte darauf und wählen Sie Eigenschaften. Setzen Sie folgende Eigenschaften: ID = Add Text = Add a new record ToolTip = Adds a new inventory master Doppelklicken Sie nun bitte auf das LinkButton-Control. Ein leerer Methodenrumpf mit dem Namen Add_Click ist in der Code-Behind-Datei erzeugt worden. Wir werden den Code später vervollständigen. Zurzeit sieht das Codeskelett so aus: private void Add_Click(object sender, System.EventArgs e) { } Konvertieren Sie die Spalten in Vorlagenspalten (Template Columns)
Obwohl das DataGrid schon jetzt funktioniert, ist es besser, die Spalten in Vorlagenspalten umzuwandeln. Der größte Vorteil besteht darin, dass wir IDs mit den EditItemTemplates assoziieren können, welche dazu benutzt werden können, im Aktualisieren-Modus auf Spaltendaten zuzugreifen. In der Grid1_Update-Methode sind wir gezwungen, auf die Spaltendaten zuzugreifen, um einen Update-SQL-Befehl zu erzeugen. Wenn keine IDs vorhanden sind, haben wir einen Bezug auf die Controls, wenn wir die Indexwerte folgendermaßen benutzen: TextBox t; t = (TextBox)e.Item.Cells[2].Controls[0]; String code_display = " code_display = '" + t.Text.Trim() + "'," ;
CRUD-Applikationen mit Visual Studio.NET 20 Aus irgendeinem Grund klappt das nicht immer wie erwartet, wohingegen die Benutzung der ID des Controls zuverlässig funktioniert. Wie Sie später noch sehen werden, können Sie die FindControl-Methode eines Controls dazu verwenden, um ein Control einer spezifizierten ID innerhalb eines DataGrids zu lokalisieren. Das kann dann auf eine TextBox gecastet werden und seine Text-Attribute können auf diese Weise extrahiert werden: t = (TextBox) e.Item.FindControl("editCode_Display"); String code_display = " code_display = '" + t.Text.Trim() + "'," ;
Um diese Spalten in Vorlagenspalten umzuwandeln, gehen Sie bitte zum Eigenschaftengenerator zurück und selektieren Sie Spalten (Columns). Wählen Sie jeweils einzeln die Spalten code_display, rate, uom und opening in der Listbox Ausgewählte Spalten und klicken Sie auf den Hyperlink Konvertiert die Spalte in eine Vorlagenspalte (Convert this column into a Template Column) im unteren Bereich. In der HTML-Ansicht sollten Sie nun noch den EditItemTemplates eindeutige IDs vergeben. Sobald Sie lostippen, werden Sie feststellen, dass VS versuchen wird, Ihre Eingabe für Sie zu vervollständigen. Jede Template-Spalte hat ein ItemTemplate und ein zugehöriges EditItemTemplate. Dieses EditItemTemplate kann jede Art von ASP.NET- Control sein (z.B. eine TextBox oder ein DropDownList-Control), wohingegen das ItemTemplate ein LabelControl ist. Das ItemTemplate ist das Control, das anfangs im DataGrid in Erscheinung tritt. Wenn Sie auf den Hyperlink Edit im DataGrid klicken, erscheint das EditItemTemplate. Da dies ein editierbares Control ist, können Sie die angezeigten Werte wie in einer TextBox verändern. Fügen Sie bitte die folgenden IDs unter dem EditItemTemplate hinzu: Die code_display Spalte, die ID wird editCode_display sein. Die rate Spalte, die ID wird editRate sein. Die uom Spalte, die ID wird editUom sein. Die opening Spalte, die ID wird editOpening sein.
415
ASP.NET 416
20 Das -Elementtag sollte in der HTML-Ansicht so aussehen:
CRUD-Applikationen mit Visual Studio.NET 20
417
Das Attribut AutoGenerateColumns des DataGrids auf False stellen Abb. 20.8
Wenn Sie das Webformular zu diesem Zeitpunkt ausprobieren, werden Sie sicher feststellen, dass jede Spalte zweimal im DataGrid vorkommt. Das kommt daher, dass das DataGrid zusätzlich zu unseren Vorlagenspalten weitere Spalten autogeneriert. Sie können dies aber verhindern, indem Sie entweder das Attribut AutoGenerateColumns des DataGrids auf False setzen oder indem Sie in dem Kontrollkästchen Spalten
ASP.NET 418
20 automatisch zur Laufzeit erstellen im Eigenschaftengenerator das Häkchen entfernen (siehe Abbildung 20.8). Dem HTML-Code wurde nun ein Attribut AutoGenerateColumns=”False" hinzugefügt. Es befindet sich im DataGrid-Elementtag:
Das Add-Panel Wir benötigen nun noch ein Panel, auf dem sich eine Reihe von TextBox- und Label-Controls befinden. Diese besitzen die Fähigkeit, neue Zeilen in die stock_master-Tabelle einzufügen. Der Grund, aus dem wir diese Controls auf einem Panel unterbringen wollen, ist, weil wir durch das Setzen des Visible-Attributs des Panels auf false die TextBox-Controls und die LabelControls ausblenden können. Ziehen Sie bitte ein Panel aus der ToolBox auf das Webformular. Rechtsklicken Sie bitte auf das Panel und wählen Sie Eigenschaften (Properties). Geben Sie es die ID AddPanel und setzen Visible auf False. Ziehen jetzt noch vier Label-Controls und vier TextBox-Controls auf das Panel. Sie sollten sie untereinander anordnen. Um sie im Panel anzuordnen, ziehen Sie zunächst eine TextBox und ein Label hinein. Rechtsklicken und kopieren Sie bitte das Label, um es dann eine Zeile darunter einzufügen. Strecken sie das zweite Label mit der Maus, bis es mit dem ersten Label gerade abschließt. Nun fügen Sie die TextBox mit Copy & Paste ein, so dass sie sich in der zweiten Zeile neben das Label gesellt. Wiederholen Sie das, bis alle Controls in der gewünschten Ordnung auf dem Formular aneinandergereiht sind. Sie müssen dies so ausführen, damit die Komponenten ordentlich nebeneinander aufgereiht werden. Die TextBox- Controls werden alle die gleiche Größe haben, jedoch werden die Label- Controls je nach der Beschriftung, die Sie haben wollen, unterschiedlich groß ausfallen. Deshalb müssen Sie die Label-Controls mit Ihrer Maus bearbeiten, bis sie sich bündig ausrichten. Sie können
CRUD-Applikationen mit Visual Studio.NET 20 auch mit den Strg-Pfeiltasten die Controls verschieben und mit den Shift-Pfeiltasten ihre Größe verändern. Geben Sie bitte den TextBox-Controls die entsprechenden IDs AddCode_Display, AddRate, AddUom und AddOpening. Sie erreichen dies, indem Sie darauf rechtsklicken und Eigenschaften (Properties) und dann anschließend ID wählen. Gügen Sie bitte noch ein Button-Control hinzu und geben Sie ihm die ID SaveNew. Doppelklicken Sie darauf, um einen Methodenrumpf zu erschaffen, der folgendermaßen aussehen sollte: private void SaveNew_Click(object sender, System.EventArgs e) { }
Diese Methode werden wir etwas später noch programmieren. Ich habe auch noch zusätzlich einen leeren Label hinzugefügt, um das Button- Control in der Mitte des Formulars zu platzieren.
Die Command-Methoden des DataGrid spezifizieren Spezifizieren Sie OnEditCommand, OnCancelCommand, OnUpdateCommand und OnDeleteCommand innerhalb des DataGridElementtags. Diese Attribute des DataGrids spezifizieren die Methode, die aufgerufen wird, sobald der Benutzer auf die Hyperlinks Bearbeiten, Abbrechen, Aktualisieren oder Löschen klickt. Dazu müssen Sie das Formular in der HTML-Ansicht öffnen und diese Attribute eigenhändig innerhalb des DataGridElementtags eintragen:
419
ASP.NET 420
20
Methoden Jetzt werden wir einige Methoden für das Webformular programmieren. Offnen Sie die Code-Behind-Datei (StockMaster.aspx.cs) und ergänzen Sie sie noch mit folgenden Methoden: Die Bind()-Methode füllt den Datensatz mit der Fill-Methode des DataAdapters. Sie bindet auch das DataGrid an den Datensatz: Listing 20.1: Die Bind-Methode public void Bind() { sqlDataAdapter1.Fill(dsStock1); DataGrid1.DataSource=dsStock1; DataGrid1.DataBind(); if (AddPanel.Visible==true) {AddPanel.Visible=false;} }
Die Bind-Methode wird im Page_Load-Event aufgerufen: private void Page_Load(object sender, System.EventArgs e) { if (! IsPostBack) { Bind(); } }
Wenn Sie das Webformular ausführen (S+%), sollten Sie das Formular in Ihrem Browser wie in Abbildung 20.9 gezeigt sehen. Die Grid1_Edit-Methode wird aufgerufen, sobald der Benutzer den Edit-LinkButton klickt. Sie setzt den EditItemIndex des DataGrid auf die angeklickte Zeile und ruft die Bind-Methode folgendermaßen auf:
CRUD-Applikationen mit Visual Studio.NET 20
421
Das Stock-MasterFormular zeigt die Daten an Abb. 20.9
Das Grid1_Cancel-Event wird durch das Klicken des Benutzers auf den Cancel-LinkButton im Edit-Modus des DataGrid aktiviert. Diese Methode setzt einfach den EditItemIndex auf -1, was im Gegenzug dem DataGrid mitteilt, dass keine Zeile mehr selektiert ist und dass es den Update-Modus schließen soll. Anschließend wird die Bind-Methode aufgerufen, um das DataGrid zu aktivieren. Listing 20.3: Grid1_Cancel public void Grid1_Cancel(Object sender, DataGridCommandEventArgs e) { DataGrid1.EditItemIndex = -1; Bind(); }
ASP.NET 422
20 Das Grid1_Delete-Event wird durch einen Klick auf den Delete-LinkButton des DataGrids abgefeuert. Dieses Event extrahiert den Primärschlüssel der angeklickten Zeile und erstellt einen SQL-Delete-Befehl, der dann an die RunSql-Methode zur Weiterverarbeitung übergeben wird. Schließlich wird der EditItemIndex auf -1 gesetzt und das DataGrid durch Aufruf der Bind-Methode aktualisiert. Listing 20.4: Grid1_Delete public void Grid1_Delete(Object sender, DataGridCommandEventArgs e) { int Key=(int)DataGrid1.DataKeys[(int)e.Item.ItemIndex]; String code_value = Key.ToString() ; String s = "Delete from stock_master"; s +=" where code_value = " + code_value; RunSql(s); DataGrid1.EditItemIndex = -1; Bind(); }
Die Grid1_Update-Methode wird abgefeuert, falls der Benutzer mit den Änderungen der Daten im Update-Modus zufrieden ist und anschließend auf den Update-LinkButton klickt. Diese Methode extrahiert den Primärschlüssel, indem sie beim DataKey-Attribut der angeklickten Zeile nachschaut. Anschließend nutzt sie die FindControl-Methode eines Controls, um die ID innerhalb des DataGrids zu lokalisieren. Das zurückgegebene Objekt wird auf eine TextBox gecastet und das Text-Attribut, wie weiter unten gezeigt, extrahiert. Ein SQL-Update-Befehl wird nun aus den extrahierten Text-Attributen des TextBoxControls erstellt. Dieser wird nun an die RunSql-Methode für die eigentliche Ausführung weitergeleitet. Letztendlich wird der EditItemIndex auf -1 gesetzt und die Bind-Methode aufgerufen, um das DataGrid zu aktualisieren. Listing 20.5: Grid1_Update public void Grid1_Update(Object sender, DataGridCommandEventArgs e) { TextBox t;
CRUD-Applikationen mit Visual Studio.NET 20 //Das ist der Primärschlüssel int Key=(int)DataGrid1.DataKeys[(int)e.Item.ItemIndex]; String code_value = " code_value =" + Key.ToString() ; t = (TextBox) e.Item.FindControl("editCode_Display"); String code_display = " code_display = '" + t.Text.Trim() + "'," ; t = (TextBox) e.Item.FindControl("editRate"); String rate = " rate = " + t.Text.Trim() + "," ; t = (TextBox) e.Item.FindControl("editUom"); String uom = " uom ='" + t.Text.Trim() + "'," ; t = (TextBox) e.Item.FindControl("editOpening"); String opening = " opening = " + t.Text.Trim() ; String s = " Update stock_master Set"; s += code_display +rate +uom ; s += opening ; s += " Where " + code_value; RunSql(s); DataGrid1.EditItemIndex = -1; Bind(); }
Diese Methode wird dazu verwendet, die SQL-Befehle Insert, Update und Delete auszuführen. Listing 20.6: RunSql public String RunSql( string vsql) { try { SqlCommand mycommand = new SqlCommand(vsql,sqlConnection1); sqlConnection1.Open(); mycommand.ExecuteNonQuery(); sqlConnection1.Close(); } catch(Exception e)
Das Add_Click-Event wird abgefeuert, sobald der Benutzer auf den Add-LinkButton klickt. Dieses Event initialisiert die TextBox-Controls mit den Standardwerten und zeigt diese an, indem es das Visible-Attribut des AddPanel auf True setzt. Listing 20.7: Add_Click private void Add_Click(object sender, System.EventArgs e) { AddPanel.Visible=true; AddCode_Display.Text= ""; AddRate.Text= "0"; AddUom.Text= ""; AddClosing.Text= "0"; }
Das SaveNew_Click-Event wird abgefeuert, sobald der Benutzer auf den SaveNew-Button, der sich auf dem AddPanel befindet, klickt. Dieses Event setzt zuerst die Standardwerte in allen vom Benutzer leer gelassenen TextBox-Controls. Danach erstellt es einen SQL-Befehlsaufruf an die gespeicherte Prozedur p_stock_master und übergibt ihr die benötigten Parameter. Diese Parameter erhält es durch Extrahieren der Text-Attribute der verschiedenen TextBox-Controls. Sie müssen dieser Prozedur ein code_value NULL übergeben, um einen neuen Eintrag einfügen zu können. Der Execute-Aufruf wird dann an die RunSql-Methode weitergeleitet, welche für die eigentliche Verarbeitung zuständig ist. Nun wird noch der EditItemIndex auf -1 gesetzt und die Bind- Methode wird aufgerufen, um das DataGrid zu aktualisieren. Listing 20.8: SaveNew_Click private void SaveNew_Click(object sender, System.EventArgs e) { if (AddCode_Display.Text.Length == 0)
CRUD-Applikationen mit Visual Studio.NET 20 { messages.Text="Leider kann der Konto Name (Account Name) nicht leer sein"; return ; } if (AddRate.Text.Length == 0) {AddRate.Text= "0";} if (AddUom.Text.Length == 0) {AddUom.Text= "";} if (AddClosing.Text.Length == 0) {AddClosing.Text= "0";} String s = "p_stock_master ausführen"; s += " @code_value=NULL," ; s += " @code_display = '" + AddCode_Display.Text + "',"; s += " @rate = " + AddRate.Text + ","; s += " @uom = '" + AddUom.Text + "',"; s += " @closing = " + AddClosing.Text ; messages.Text= ""; RunSql(s); DataGrid1.EditItemIndex = -1; Bind(); }
Damit wäre unsere Betrachtung über CRUD-Applikationen mit Visual Studio.NET zum Abschuss gebracht. Sie haben sicherlich erkannt, welche signifikanten Vorteile ein solches Tool beim Programmieren bringt. Ich muss zugeben, dass ich sehr von der Autovervollständigung der Elementtags in der HTML-Ansicht von Visual Studio.NET beeindruckt bin. Ich muss nicht mehr die einzelnen Attribute der Tags auswendig können, da sie angezeigt werden, sobald ich anfange zu tippen. Auch die Möglichkeit, die Controls nach Lust und Liebe mit der Maus zu verschieben, gehört zu den Features, die ich sehr mag.
425
Einen Webservice mit Visual Studio.NET erstellen
Den generischen DatenbankWebservice erstellen
428
Der Webservice wird aufgebaut
430
Den Webservice aus einem Webformular aufrufen
436
Projekt 2 – Zusammenfassung
446
ASP.NET 428
21 In diesem Kapitel werde ich Ihnen zeigen, wie man einen Webservice mit Visual Studio.NET entwickelt. Sie werden erst einmal einen generischen Datenbankservice in C# erstellen. Dieser Webservice wird eine Methode mit dem Namen Populate haben. Diese Methode wird SQL-SELECT-Befehle entgegennehmen. Aufgrund dieses SELECT-Befehls wird der Webservice diesen ausgewählten Datensatz an das aufrufende Objekt zurückgeben. Der Webservice wird noch eine RunSql-Methode haben, die dazu verwendet wird, eine Action Query (einen Insert-, einen Update- oder einen Delete-Befehl) an die Datenbank weiterzuleiten. Sie werden dann noch ein Webprojekt entwickeln, das sich diesen Service zunutze machen wird. Dieses Projekt wird die Populate-Funktion des Webservices mit den entsprechenden Parametern aufrufen und daraufhin einen Datensatz mit dem Resultat erhalten. Ein DataGrid wird dann an den Datensatz gebunden, um die angeforderten Daten der Zeilen zu visualisieren. Sie werden auch die RunSql-Methode testen, indem einige Zeilen eingefügt und gelöscht werden.
Den generischen Datenbank-Webservice erstellen 1
Starten Sie Visual Studio.NET.
2
Wählen Sie Datei/Neu/Projekt (File/New/Project).
3
Wählen Sie C# Projects auf der linken und ASP.NET Webdienst auf der rechten Seite. Abbildung 21.1 zeigt den aktuellen Zustand von VS.NET. (In der Release Candidate Version von Visual Studio.NET kann man den Projektnamen hinter dem Pfad anfügen – in diesem Falle http://localhost/CSharpdbService. Das Namensfeld ist deaktiviert, wird aber automatisch den eingetippten Projektnamen anzeigen.)
4
Wählen Sie bitte den Namen CSharpdbService.
Einen Webservice mit Visual Studio.NET erstellen 21
429
Ein C#- ASP.NETWebservice wird erstellt Abb. 21.1
5
Visual Studio.NET erstellt eine neue Projektmappe (Solution), welche ein Verweiseverzeichnis und vier Dateien kreiert. Web.config ist eine XML-Datei, die verschiedene Konfigurationssoptionen (z.B. Session Timeout Interval) enthält und den Webservice zur Laufzeit kontrolliert. CSharpdbService.vsdisco ist eine XML-Datei, die für die dynamische Suche nach Webservices durch Clients zuständig ist. Das heißt, wenn Sie diesen Service in einem anderen Projekt benutzen wollen, navigieren Sie zu dieser Datei und VS.NET erstellt eine Referenz zu diesem Service für Sie. In der Datei Global.asax residieren projektweite Eventhandler wie ApplicationStart und ApplicationEnd.
6
Rechtsklicken Sie auf Service1.asmx und selektieren Sie Öffnen mit (OpenWith) und anschließend Quellcode-Editor (Text). Oder navigieren Sie zu dem Applikationsverzeichnis und öffnen Sie die Datei in Ihrem Editor. Sie werden entdecken, dass diese Datei auf den Code einer anderen Datei (Service1.asmx.cs) verweist:
ASP.NET 430
21 Wenn sie so weit gekommen sind, haben Sie alle Teile für den Webservice beisammen. Sie müssen jetzt nur noch den Code für die Webservice-Datei (asmx) programmieren.
Der Webservice wird aufgebaut In diesem Teil werden wir einen Webdienst mit dem Namen dbService erstellen. Er wird die Methoden Populate und RunSql besitzen. 1
Löschen Sie bitte die automatisch erstellte Datei Service1.asmx und fügen Sie eine neue Webdienstdatei hinzu. Dazu rechtsklicken Sie bitte auf den Projektnamen und wählen Hinzufügen/Webdienst hinzufügen (Add/Add Webservice). Nennen Sie diesen Webdienst dbService.asmx.
2
Ein Webdienstgrundgerüst wird erschaffen und beinhaltet einige Namensräume. Diese Namespaces sind nun in der Datei dbService.asmx.cs vorhanden: using using using using using using using
Da Sie mit Datenbanken kommunizieren wollen, müssen Sie entweder die Namensräume für SQL oder für OleDb Managed Provider importieren. Ich habe mich für das zweite entschieden, da ich den dbService-Webservice so generisch wie möglich halten möchte. Der SQL Managed Provider würde uns auf Microsofts SQL Server-Datenbank einschränken. Sie sollten den OleDb Managed Provider-Namespace auf folgende Weise importieren: using System.Data.OleDb;
Der Webservice ist im Namensraum von CSharpdbService erstellt:
Einen Webservice mit Visual Studio.NET erstellen 21 namespace CSharpdbService { //Hier steht der gesamte Code }
Die Klasse heißt dbService und erbt von System.Web.Services.WebService. Die Vererbung wird durch den Doppelpunkt (:) vor der Basisklasse angezeigt: public class dbService : System.Web.Services.WebService
dbService hat ein connStr-Feld, welches Sie als private deklarieren müssen: private String connStr;
Die dbService-Klasse hat einen Konstruktor ohne Parameter. Ein Konstruktor ist eine Methode, die den gleichen Namen wie die Klasse hat. Jede Klasse besitzt einen Standardkonstruktor, der keine Parameter akzeptiert. Es steht jeder Klasse frei, zusätzliche Konstruktoren, die beliebige Parameter akzeptieren, zu beherbergen. Im Konstruktor wird der ConnectionString folgendermaßen auf das private Feld connStr gesetzt: public dbService() { InitializeComponent(); connStr = "Provider=SQLOLEDB; Data Source=(local); "; connStr = connStr+" Initial Catalog=PUBS;User ID=sa;Password="; } 3
Zwei Webmethoden müssen in der dbService-Klasse erstellt werden. Eine Webmethode wird wie eine normale C#-Methode erstellt, ist jedoch mit einem vorangestellten Attribut [WebMethod] versehen. Die erste Methode heißt Populate und sendet einen SELECT- SQL-Befehl an die Datenbank und erhält dafür das resultierende DataSet zurück. Dieses DataSet kann dann dazu benutzt werden, um ein Bound-Control wie
431
ASP.NET 432
21 das DataGrid zu binden. Hier ist die Auflistung der Methode: [WebMethod] public DataSet Populate(string SQL) { //für abfragen, die Daten zurückliefern //und für bindende Controls OleDbConnection myConnection = new OleDbConnection(connStr); OleDbDataAdapter myCommand = new OleDbDataAdapter(SQL, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "vTable"); return ds; }
Die zweite Methode RunSQL sendet ein Action Query an die Datenbank. Eine Action Query ist, wie Sie sicher wissen werden, eine SQL Query, wie z.B. Insert, Delete oder Update. Sie führt irgendeine Aktion in der Datenbank aus, ohne Daten zurückzuerhalten. Diese Methode akzeptiert ein gültiges SQL Insert, Delete oder Update Query und leitet es an die Datenbank weiter. Hier ist der Code dieser Methode: [WebMethod] public String RunSQL( string vsql) { try { OleDbConnection myConnection = new OleDbConnection(connStr); OleDbCommand mycommand = new OleDbCommand(vsql,myConnection); myConnection.Open(); mycommand.ExecuteNonQuery(); myConnection.Close(); return("OK"); } catch(Exception e) { string ret = "Exception: " + e.ToString() ;
Einen Webservice mit Visual Studio.NET erstellen 21 return ret; } }
Ihr Webdienst ist nun fertig gestellt. Sie können ihn mit einem rechten Mausklick auf die asmx-Datei des Webservices (dbService.asmx) im Projektmappen-Explorer und die Auswahl von Als Startseite festlegen (Set as Start Page) ausprobieren. Jetzt drücken Sie noch % und die Webservice-Testseite sollte wie in Abbildung 21.2 aussehen.
Die Testseite des Webservices Abb. 21.2
Klicken Sie nun auf die Methode Populate. Auf der nun erscheinenden Seite tragen sie bitte folgenden Parameterwert ein: Select * from authors
Wenn Sie auf die Schaltfläche Aufrufen (invoke) klicken, werden alle Aufzeichnungen der authors-Tabelle im XML-Format angezeigt:
433
ASP.NET 434
21
Die Populate-Methode gibt Datenbankzeilen als XML aus Abb. 21.3
Hier ist der vollständige Code des Webservices: Listing 21.1: dbService.asmx using using using using using using using using
namespace CSharpdbService { /// /// Zusammenfassungsbeschreibung für dbService. /// public class dbService : System.Web.Services.WebService { private String connStr; public dbService() { //CODEGEN: Dieser Aufruf ist für den ASP.NETWebdienst-Designer erforderlich. InitializeComponent(); connStr = "Provider=SQLOLEDB; Data Source=(local); ";
Einen Webservice mit Visual Studio.NET erstellen 21 connStr = connStr+" Initial Catalog=PUBS;User ID=sa;Password="; } #region Component Designer generated code /// /// Erforderliche Methode für die Designerunterstützung. /// Der Inhalt der Methode darf nicht mit dem CodeEditor geändert werden. /// private void InitializeComponent() { } #endregion /// /// Die verwendeten Ressourcen bereinigen. /// protected override void Dispose( bool disposing ) { } [WebMethod] public DataSet Populate(string SQL) { //Für Abfragen, die Daten zurückerhalten, //und für bindende Controls OleDbConnection myConnection = new OleDbConnection(connStr); OleDbDataAdapter myCommand = new OleDbDataAdapter(SQL, myConnection); DataSet ds = new DataSet(); myCommand.Fill(ds, "vTable"); return ds; } [WebMethod] public String RunSQL( string vsql) { try { OleDbConnection myConnection = new OleDbConnection(connStr); OleDbCommand mycommand = new
Den Webservice aus einem Webformular aufrufen In diesem Teil werde ich eine Clientapplikation entwickeln, die den Webdienst, den wir im letzten Abschnitt gebaut haben, benutzen wird. Diese Anwendung ist eine C#-ASP.NET-Webanwendung und wird wie in den nächsten Schritten beschrieben erstellt: 1
Starten Sie Visual Studio.NET und öffnen Sie die CSharpdbService-Projektmappe (Solution), die wir zuvor erstellt haben. (Diese Projektmappe muss geöffnet werden, da Sie das neue Projekt dort einfügen werden.)
2
Wählen Sie Datei/Neu/Projekt (File/New/Project).
3
Wählen Sie C# Projects auf der linken und ASP.NET Webdienst auf rechten Seite. Aktivieren Sie das Optionsfeld mit der Bezeichnung Zu Projektmappe hinzufügen (Add to Solution – siehe Abbildung 21.4). Dadurch wird dieses neue Projekt in die bestehende Projektmappe (Solution) eingefügt und Sie können mit beiden Projekten gleichzeitig in einer Projektmappe arbeiten. Nennen Sie dieses neue Projekt TheClient. In der Release Candidate Version von Visual Studio.NET kann man den Projektnamen hinter dem Pfad anfügen
Einen Webservice mit Visual Studio.NET erstellen 21
437
– in diesem Falle http://localhost/TheClient. Das Namensfeld ist deaktiviert, wird aber automatisch den eingetippten Projektnamen anzeigen.
Eine neue C#-ASP.NETWebapplikation erstellen Abb. 21.4
So werden beide Projekte im Projektmappen-Explorer aussehen:
Beide Projekte werden im Projektmappen-Explorer angezeigt Abb. 21.5
ASP.NET 438
21 4
Rechtsklicken Sie bitte auf das aspx-Formular mit dem Namen WebFrom1.aspx, welches automatisch erschaffen wurde, und legen Sie es als Startseite fest. Sie müssen auch noch das Projekt TheClient als Startprojekt festlegen. Dazu klicken Sie im Projektmappen bitte wieder mit der rechten Maustaste auf den Projektnamen und wählen Als Startprojekt festlegen (Set as StartUp Project).
5
Rechtsklicken Sie bitte auf eine freie Stelle auf dem Formular und wählen Sie Eigenschaften (Properties). Stellen Sie sicher, dass das Page_Layout-Attribut auf GridLayout gesetzt ist. Mit GridLayout können Sie die Controls visuell mit der Maus auf dem Formular positionieren. Ziehen Sie bitte ein DropDownList-Control, zwei Label-Controls, ein DataGrid-Control und zwei Button-Controls auf das Formular. Rechtsklicken Sie auf jeden der Buttons und wählen Sie Eigenschaften (Properties). Dort geben Sie ihnen die IDs Populate und RunSql. Das erste Label-Control wird für den Titel dieses Formulars benutzt. Dem zweiten Label-Control wird eine ID von messages gegeben. Rufen Sie die Eigenschaften des DropDownList-Controls auf (wie immer mit Rechtsklick und Eigenschaften (Properties)). Finden Sie das items-Attribut und klicken Sie auf die drei Punkte daneben, um den ListItem-AuflistungsEditor aufzurufen. Klicken Sie auf die Schaltfläche Hinzufügen unten auf dem Panel. Nun müssen Sie noch einige SELECT-SQL-Abfragen in die Value Input Box eintragen. Sie können z.B. Folgendes eintragen: Select Select Select Select
* * * *
from from from from
authors titles publishers pub_info where pub_id = "9999"
Der Benutzer wird einen dieser SELECT-Aufrufe aus der Drop-Down-List auswählen, die dann wiederum an die Populate-Methode des Webservices weitergeleitet werden. Der Webservice wird ein DataSet zurückgeben, das das DataGrid binden wird. Ich habe das Webformular verschönert, indem ich mit der rechten Maustaste auf eine leere Stelle geklickt und
Einen Webservice mit Visual Studio.NET erstellen 21 die Eigenschaften aufgerufen habe. Das ruft das Fenster Dokument-Eigenschaftenseiten (Document Property Pages) auf, wo ich dann auf dem Feld Farbe und Ränder (Colors and Margins) auf die drei Punkte neben der Box Hintergrundfarbe geklickt habe. Dort habe ich dann eine schicke Farbe für das Formular ausgewählt. Ich habe mich für die Farbe #ffcc99 entschieden. Danach wollte ich noch das DataGrid verschönern. Also rechtsklickte ich auf das DataGrid und begab mich in das Fenster Eigenschaften (Properties), wo ich weiter unten auf den Hyperlink Autom. Formatierung klickte. Ich entschied mich für das Farbschema Klassisch 2 aus der mir präsentierten Liste von Stilen. Das Webformular sollte nun wie in Abbildung 21.6 ausschauen.
Design des ClientWebformulars Abb. 21.6
6
Sie müssen eine Referenz auf den CSharpdbService erstellen, um ihn in der Anwendung benutzen zu können. Dazu wählen Sie bitte Projekt/Webverweis hinzufügen (Project/Add Web Reference) aus dem Hauptmenü. Oder Sie klicken auf den Projektnamen im Projektmappen-Fenster und wählen Webverweis hinzufügen (Add Web Reference).
439
ASP.NET 440
21 Ein neues Fenster mit dem Namen Webverweis hinzufügen erscheint. Auf der linken Seite klicken sie bitte auf den Hyperlink Webverweise auf dem Lokalen Webserver. Dies sollte Ihnen alle Discovery-Dateien (*.disco oder *.vsdisco) anzeigen. Daraus wählen Sie bitte CSharpdbService.vsdisco aus. Visual Studio.NET wird den Webservice entdecken und das Resultat wie in Abbildung 21.7 präsentieren. Sie können auch den vollständigen Pfad (localhost) des Webservices angeben und ein ?wsdl hinten in der Adressleiste anfügen. Zum Beispiel befindet sich mein Webservice auf http://localhost/CSharpdbService/dbService.asmx? wsdl.
Die Suche nach Webservices Abb. 21.7
Klicken Sie nun bitte auf die Schaltfläche Verweis hinzufügen am unteren Rand des Fensters. Eine Referenz auf den Webservice wird nun im Projektmappen-Explorer zusätzlich angezeigt.:
Einen Webservice mit Visual Studio.NET erstellen 21
441
Referenz auf den Webservice im Solution Explorer Abb. 21.8
7
Um das Ganze zum Laufen zu bringen, fehlt noch ein wenig Code. Als Erstes deklarieren und instanziieren Sie den Webservice folgendermaßen: private localhost.dbService mydbService = new localhost.dbService();
Programmieren sie nun den Click-Event von Button1. Bitte beachten Sie, dass sich die Click-Events der beiden Buttons hinter der Methode InitializeComponent()befinden müssen, da die Delegates für diese Click-Events erst in dieser Methode erzeugt werden. Wenn Sie sie doch vor diese Methode platzieren sollten, werden sie nicht abgefeuert. private void Populate_Click(object sender, System.EventArgs e) { DataSet ds = new DataSet(); String s = DropDownList1.SelectedItem.ToString(); ds = mydbService.Populate(s); DataGrid1.DataSource=ds; DataGrid1.DataBind(); }
ASP.NET 442
21 Dies testet die Populate-Methode. Der vom Benutzer ausgewählte SELECT-Befehl wird an die Populate-Methode des Webservices gesendet. Ein Datensatz wird zurückgegeben, welcher an das DataGrid gebunden wird. Nachdem Sie % gedrückt haben, um die Populate-Methode zu testen, sollte das Webformular wie im Screenshot in Abbildung 21.9 aussehen.
Auswahl eines SELECTBefehls zeigt das Ergebnis im DataGrid an Abb. 21.9
Um die RunSQL-Methode des Webservice zu testen, muss der folgende Code hinter das Click-Event des RunSql-Buttons angefügt werden: private void RunSql_Click(object sender, System.EventArgs e) { string s; String ret; s = "Delete from pub_info where pub_id = '9999'"; ret=mydbService.RunSQL(s); messages.Text= ret; //füge eine zufällige Zahl ein Random r = new Random(); s = " insert into pub_info(pub_id,pr_info) values("; s = s + " '9999','" + r.Next(1000).ToString() + "')"; ret = mydbService.RunSQL(s); messages.Text= ret;
Einen Webservice mit Visual Studio.NET erstellen 21
443
//Refresh the DataGrid to show changes DataSet ds = new DataSet(); s = "Select * from pub_info where pub_id = '9999'"; ds = mydbService.Populate(s); DataGrid1.DataSource=ds; DataGrid1.DataBind(); }
Diese Methode benutzt die Webservice-Methode RunSql, um zuerst die Zeilen der pub_info-Tabelle mit der pub_id von 9999 zu löschen. Anschließend wird mit Hilfe der Random-Klasse eine zufällige Zahl zwischen 1 und 1000 erzeugt. Dies macht die Next-Methode der Random-Klasse, basierend auf dem Parameter, den es erhält. Dieser Parameter spezifiziert das obere Limit der Zufallszahl, das ich hier auf 1000 gesetzt habe. Diese Zahl wird in die pr_info-Spalte der pub_info-Tabelle mit der pub_id von 9999 eingefügt. Schließlich wird die Populate-Methode aufgerufen, um die veränderte Zeile zurückzugeben. Das DataGrid ist wieder einmal an den zurückgegebenen Datensatz gebunden und zeigt deswegen den aktuellsten Wert an. Sie können diese Methode testen, indem sie auf die Schaltfläche RunSql Test klicken. Jedes Mal, wenn Sie das tun, werden Sie eine neue Zahl in der pr_info-Spalte angezeigt bekommen (siehe Abbildung 21.10).
Der Button RunSql Test fügt eine Zufallszahl in die Tabelle ein. Abb. 21.10
ASP.NET 444
21 Hier ist das gesamte WebForm1.aspx.cs:
Listing
der
Code-Behind-Datei
Listing 21.2: WebForm1.aspx.cs using using using using using using using using using using
namespace TheClient { /// /// Zusammenfassungsbeschreibung für WebForm1. /// public class WebForm1 : System.Web.UI.Page { protected System.Web.UI.WebControls.DataGrid DataGrid1; protected System.Web.UI.WebControls.Label Label1; protected System.Web.UI.WebControls.DropDownList DropDownList1; protected System.Web.UI.WebControls.Button RunSql; protected System.Web.UI.WebControls.Button Populate; private localhost.dbService mydbService = new localhost.dbService(); public WebForm1() { Page.Init += new System.EventHandler(Page_Init); } private void Page_Load(object sender, System.EventArgs e) { // Setze Benutzercode, um die Seite zu initialisieren
Einen Webservice mit Visual Studio.NET erstellen 21 } private void Page_Init(object sender, EventArgs e) { // //CODEGEN: Dieser Aufruf ist für den ASP.NETWebdienst-Designer erforderlich. InitializeComponent(); } #region Web Form Designer generated code /// /// Erforderliche Methode für die Designerunterstützung. /// Der Inhalt der Methode darf nicht mit dem CodeEditor geändert werden. /// private void InitializeComponent() { this.Populate.Click += new System.EventHandler(this.Populate_Click); this.RunSql.Click += new System.EventHandler(this.RunSql_Click); this.Load += new System.EventHandler(this.Page_Load); } #endregion private void Populate_Click(object sender, System.EventArgs e) { DataSet ds = new DataSet(); String s = DropDownList1.SelectedItem.ToString(); ds = mydbService.Populate(s); DataGrid1.DataSource=ds; DataGrid1.DataBind(); } private void RunSql_Click(object sender, System.EventArgs e) { string s;
445
ASP.NET 446
21 String ret; s = "Delete from pub_info where pub_id = '9999'"; ret=mydbService.RunSQL(s); messages.Text = ret; //erzeugt eine Zufallszahl Random r = new Random(); s = " insert into pub_info(pub_id,pr_info) values("; s = s + " '9999','" + r.Next(1000).ToString() + "')"; ret=mydbService.RunSQL(s); messages.Text = ret;
//Aktualisiere das DataGrid, um die Änderungen anzuzeigen DataSet ds = new DataSet(); s = "Select * from pub_info where pub_id = '9999'"; ds = mydbService.Populate(s); DataGrid1.DataSource=ds; DataGrid1.DataBind(); } } }
Webservices mit Visual Studio.NET zu erstellen ist ziemlich einfach. Die verfügbaren Assistenten und Tools machen die Aufgabe der Positionierung der Komponenten zu einer Kleinigkeit. Der generierte Code ist kompakt und präzise und ich mag diesen Aspekt sehr. Ein großes Problem der früheren MicrosoftTools wie Visual InterDev war, dass sie jede Menge zusätzlich benötigte Dateien erstellten. Das ist in Visual Studio.NET nicht der Fall. Es ist stets möglich, die Dateien auch in einem TextEditor zu öffnen, um an ihnen weiterzuarbeiten.
Projekt 2 – Zusammenfassung In diesem Teil haben Sie die Möglichkeiten von Visual Studio.NET für das Rapid Application Development (RAD) kennen gelernt. Sie haben mit verschiedenen Tools, Designern und
Einen Webservice mit Visual Studio.NET erstellen 21 Komponenten dieser Umgebung experimentiert und auch eine Datenbank und eine Webservice-Applikation mit dieser Entwicklungsumgebung erzeugt. Als ein RAD-Tool ist Visual Studio.NET sehr zu empfehlen. Seine integrierte Entwicklungsumgebung ermöglicht Ihnen, mit mehreren Sprachen zu arbeiten, und hat auch einen wunderbaren Debugger. Visual Studio.NET hat seine graphische Benutzerschnittstelle von früheren Microsoft-Produkten wie Visual InterDev und Visual Basic 6x geerbt. Ein großes Problem, das man bei Visual InterDev hatte, war, dass es hinter dem Rücken einige wichtige Dateien erstellte. Erfahrene Programmierer scheuten davor zurück, dieses Programm zu benutzen, und bevorzugten lieber einfache Text-Editoren. Sie können jedoch erleichtert aufatmen, denn dies ist jetzt in Visual Studio.NET nicht mehr der Fall. Der generierte Code ist elegant und präzise und in den meisten Fällen dem ziemlich ähnlich, was Sie von Hand programmieren würden. Sie können dasselbe Tool benutzen, um in den Programmiersprachen C#, VB.NET und ASP.NET zu programmieren, und müssen keine neue Arbeitsumgebung kennen lernen. Zusammenfassend möchte ich sagen, dass dies eine schöne Entwicklungsumgebung ist, die zu besserer Entwicklungsproduktivität führen kann.