VB.NET und Datenbanken - Leseprobe (Galileo - Die-PC

Werbung
Stefan Fellner
Visual Basic .NET und
Datenbanken
Inhalt
Einleitung
13
Ein Neubeginn mit Visual Basic .NET
Update oder Neuschöpfung? 14
14
Die neue Datenbanktechnologie ADO.NET
Es war einmal ein Recordset 17
Die Verbindung zur Datenbank 18
Ein erstes Beispiel 18
Tabelle laden und darstellen 19
Verknüpfte Tabellen und XML-Daten
17
21
Dieses Buch 25
Die Beispiele dieses Buches 29
Konventionen und Symbole 30
Tipp 33
1
Visual Studio .NET
1.1
1.1.1
1.1.2
1.1.3
1.1.4
Visual Studio .NET und das .NET Framework 36
Systemvoraussetzungen 37
Koexistenz mit Visual Basic 6, Upgrade von Projekten
SQL Server und MSDE 38
IIS-Webserver 39
IIS nachträglich für das .NET Framework einrichten 40
1.2
1.2.1
Die Installation von Visual Studio .NET
MSDE installieren 42
1.3
Die Entwicklungsumgebung von Visual Studio 43
Einschränkungen der MSDE aufheben 43
Was bietet die neue IDE? 45
Der Server-Explorer 49
Systemressourcen einbinden 50
Download von zusätzlichen Datenprovidern 50
.NET Framework-Zusätze verwenden 53
1.3.1
1.3.2
1.3.3
1.3.4
35
41
1.4
Dokumentation und Selbstdokumentation
XML-Kommentare verwalten 56
1.5
.NET-Anwendungen debuggen
1.6
.NET-Anwendungen kompilieren 59
Debug und Release konfigurieren 59
Konfigurationseinstellungen 60
Kompilierreihenfolge 60
Vorkompilieren 61
Kommandozeilencompiler 62
Anwendungen mit dem .NET Framework kompilieren 64
1.6.1
1.6.2
1.6.3
1.6.4
38
54
57
Inhalt
5
1.7
1.7.1
1.7.2
Alternativen zur Visual Studio .NET-IDE
SharpDevelop 65
Webmatrix 66
1.8
1.8.1
1.8.2
1.8.3
1.8.4
.NET-Anwendungen installieren 67
Zielordner festlegen 68
Setup-Gestaltung 68
Installation auf Nicht-.NET-Systemen 69
Benutzerdefinierte Aktionen 69
Benutzereingaben während einer Installation auswerten 70
1.9
1.9.1
1.9.2
Installieren der Beispieldatenbank 71
SQL Server-Datenbank installieren 72
MySQL-Datenbank installieren 75
MySQL-Datenbanken und Berechtigungen 75
1.10
1.10.1
1.10.2
1.10.3
1.10.4
Datenbankprojekte 77
Datenbankverweise 77
Skripte und Server-Explorer 79
Tabellen und Sichten 80
Gespeicherte Prozeduren 81
Debugging im SQL Server zulassen 81
Trigger 83
Diagramme 84
Erstellungsskripte 85
Hinweis auf ORM-Diagramme 85
Befehlsdateien 86
Projektverwaltung 86
Skripte zur Datenbankadministration zusammenstellen 88
1.10.5
1.10.6
1.10.7
1.10.8
1.10.9
6
65
2
Entwickeln mit .NET
2.1
2.1.1
2.1.2
2.1.3
2.1.4
2.1.5
2.1.6
2.1.7
2.1.8
Common Language Runtime 90
Common Language Infrastructure 92
Just in Time-Compiler 92
Sprachintegration 93
Common Type System 93
Debugging 93
Strukturierte Fehlerbehandlung 94
Speicherverwaltung, Garbage Collector
Garbage Collector gezielt aufrufen 97
Interoperabilität mit COM 97
2.2
Metadaten in .NET
2.3
Namensräume und Basisklassen
2.4
2.4.1
Assemblies 100
Manifest 102
Programmatischer Zugriff auf Assembly-Informationen 103
Inhalt
89
95
98
99
2.5
2.5.1
2.5.2
2.5.3
Sicherheit 104
Sicherheit auf Code-Ebene 104
Assemblies definieren Sicherheitsbereiche
Administrieren von Rechten 106
2.6
2.6.1
2.6.2
Versionierung 107
Versionierung mit COM 107
Versionierung mit Assemblies 108
Neuere Version des .NET Frameworks verwenden 111
3
.NET, Windows und XML-Webdienste (Web Services)
3.1
3.1.1
Windows und COM
Notlösungen 118
3.2
3.2.1
Windows und .NET 119
Anforderungen an .NET 119
3.3
Interaktionen über das Internet
3.4
Ein Objektmodell für das Internet
122
3.5
XML-Webdienste (Web Services)
122
3.6
Programmierbares Internet
3.7
3.7.1
3.7.2
3.7.3
3.7.4
3.7.5
Microsofts .NET-Strategie
.NET Framework 125
.NET-Server 126
Visual Studio .NET 127
.NET Services 128
Software als Service 129
3.8
3.8.1
3.8.2
3.8.3
Plattformunabhängiges .NET
Rotor (FreeBSD) 130
Mono (Linux) 131
DotGNU (GNU/Linux) 132
3.9
3.9.1
3.9.2
Eine Infrastruktur für Webdienste 132
Web Service Description Language (WSDL)
UDDI-Verzeichnisse 135
4
Grundlagen der Datenbankbearbeitung
4.1
4.1.1
Einführung in Datenbanken
Datenmodelle 143
4.2
4.2.1
4.2.2
4.2.3
4.2.4
4.2.5
4.2.6
Entwurf relationaler Datenbanken 147
Datentabellen und Datenschlüssel 147
Tabellenverknüpfungen 148
Benennungsregeln 149
Referentielle Integrität 150
Tabellen-Indizes 151
Normalformen 152
105
115
117
121
124
124
129
133
137
140
Inhalt
7
4.3
4.3.1
4.3.2
4.3.3
4.3.4
4.3.5
4.3.6
4.3.7
4.3.8
4.3.9
4.3.10
4.4
4.4.1
4.4.2
4.4.3
4.4.4
4.4.5
4.4.6
4.4.7
4.4.8
4.4.9
4.4.10
4.4.11
4.4.12
XML-Verarbeitung 184
Die Herkunft von XML 184
Datenabfragen in XML 186
XML-Dokumente 187
XSL-Transformationen 191
XML-Elemente und Attribute 193
XML-Dateien und XSD-Schemas 194
XML und Namensräume 200
XPath-Abfragen 201
XPath-Beispiele 205
XPath-Richtungsnavigation 213
XPath-Abfragen über URL 222
XQuery 222
4.5
SQL Server-Datenbanken anlegen und verwalten 228
Verbindungen zum SQL Server 229
Datenbank anlegen 230
SQL Server zum SQL Server Enterprise Manager hinzufügen 233
Transact-SQL verwenden 235
Datenbank aktivieren/deaktivieren 236
Datenbanken sichern und wiederherstellen 237
Datenbank dbWürzen aus Datenbanksicherung erstellen 240
Benutzer- und Rechteverwaltung 240
Berechtigungen für Datenbankrollen 247
Tabellen anlegen 249
Indizes anlegen 251
Einschränkungen 252
Referentielle Integrität 253
Diagramme verwenden 254
Trigger 255
Gespeicherte Prozeduren 257
4.5.1
4.5.2
4.5.3
4.5.4
4.5.5
4.5.6
4.5.7
4.5.8
4.5.9
4.5.10
4.5.11
4.5.12
8
SQL-Grundlagen 156
Structured Query Language 157
OSQL für SQL-Anweisungen verwenden 158
Schemainformationen 159
SELECT 161
Nulldatum des SQL Servers 169
UNION 174
JOIN 175
NULL-Werte 178
Vorgabewerte für Nicht-NULL-Felder 179
INSERT 180
UPDATE 181
DELETE 182
Ausblick 183
Duplikate in einer Tabelle finden 183
Inhalt
5
ADO.NET und Datenbanken
5.1
5.1.1
Einführung in ADO.NET 263
Wege nach ADO.NET 265
5.2
5.2.1
5.2.2
5.2.3
Verbindungen zur Datenbank 268
Universaler Datenzugriff 268
Das asynchrone Modell 269
Datenprovider 270
Datenprovider-unabhängiger Code 272
Connection-Objekt 274
Datenbankverbindungen in App.config konfigurieren 277
Transaktionen 279
Command-Objekt 281
Mit Gespeicherten Prozeduren arbeiten 283
Upgrade von ADO(alt) 285
Upgrade von Visual Basic 6 und Recordsets im DataSet 288
ODBC-Datenprovider 289
DataReader 290
DataReader und Recordset 291
5.2.4
5.2.5
5.2.6
5.2.7
5.2.8
5.2.9
5.2.10
5.3
263
5.3.1
5.3.2
Datengebundene Eingabemaske 296
Assistenten-Code verwenden 297
DataForm-Assistent 298
Das Projekt auf Code-Ebene 305
5.4
5.4.1
DataSet als lokale Datenbank 315
Transportable Datenbank 315
5.5
5.5.1
5.5.2
Datenadapter für das DataSet 317
DataAdapter 317
Datenadapter mit Assistenten konfigurieren
5.6
5.6.1
5.6.2
DataSet-Objekt 333
DataSet und XML 337
DataSet und XML-Dokument
5.7
5.7.1
5.7.2
5.7.3
Datenobjekte des DataSets
DataTable 343
DataRow 353
DataView 356
5.8
5.8.1
5.8.2
5.8.3
Tabellenverknüpfungen 363
DataRelation 363
Darstellung verknüpfter Tabellen
Zugriff auf Detaildaten 369
5.9
5.9.1
5.9.2
Datenauswahl und Datenaktualisierung
CommandBuilder 371
Aktualisieren von Autowerten 373
5.10
5.10.1
5.10.2
Datenbindung und Datennavigation
BindingContext 375
Datenmaske mit Bildern 378
Bilder aus der Datenbank 379
321
341
343
367
370
375
Inhalt
9
5.11
5.11.1
Hierarchische Daten 385
Datengebundenes Treeview
5.12
5.12.1
5.12.2
5.12.3
Asynchrone Datenbankanwendungen 402
Bedeutung von Transaktionen 402
Parallelitätssteuerung in ADO.NET 405
Parallelitätsverletzung behandeln 411
6
Datenzugriffsmodule
6.1
6.1.1
Datenbankzugriff und Analyse 418
Tabellen-Analyseprogramm 419
Datenbankverbindung mit DataLink-Dialog einrichten 425
Testmodul für Gespeicherte Prozeduren 438
6.1.2
6.2
6.2.1
6.2.2
6.2.3
6.2.4
7
386
417
Datenzugriffscode (Data Access Application Block)
Aufbau des Data Access Application Blocks 446
Data Access Application Block einbinden 446
Aufruf mit SQL-Anweisung 449
Aufruf mit Gespeicherter Prozedur 453
Anwendungsbereich 458
445
Serverseitige Datenverarbeitung und Webdienste
(Web Services) 461
Webdienstbeispiele installieren oder kopieren 463
7.1
7.1.1
7.1.2
7.1.3
7.1.4
7.1.5
7.1.6
7.1.7
7.2
7.2.1
7.2.2
10
Ein datengebundener Webdienst 465
Webprojekte auf entfernten Rechnern einrichten 466
Webdienst benennen 467
Namensräume hinzufügen 467
Verbindungen zur Datenbank 468
Pfad des virtuellen Verzeichnisses 469
Webdienstfunktionen 470
Datenbankzugriff für ASP.NET zulassen 472
Erstellen und Testen des Webdienstes 473
Webdienst mit Rückgabe eines DataSets 476
Webdienst mit DataSet testen 478
Testseiten in Produktivumgebung deaktivieren 479
7.2.3
7.2.4
7.2.5
Ein Webdienstclient 480
Dropdown Gewürzliste 480
Einbinden des Webdienstes 482
.vsdisco-Dateien verwenden 484
Dropdown an Daten aus dem Webdienst binden 486
Webdienst zur Aktualisierung der Datenbank 488
Webdienstclient zur Aktualisierung der Datenbank 491
7.3
7.3.1
Daten zwischen Anwendungsebenen übertragen
Diffgram 500
Inhalt
499
7.4
7.4.1
7.4.2
7.4.3
7.4.4
XML und SQL Server 2000 506
OpenXML 508
OpenXML und ADO.NET 514
FOR XML-Abfragen 515
FOR XML im Query Analyzer 516
FOR XML und ADO.NET 530
7.5
7.5.1
7.5.2
7.5.3
7.5.4
7.5.5
7.5.6
7.5.7
Die SQLXML-Erweiterung 534
Einrichten von SQLXML 534
URL-Aufrufe 537
Abfragen an das Datenbankobjekt 538
Template-Abfragen 539
Schema-Abfragen 542
Webdienste aus dem SQL Server 543
Ausblick 545
7.6
7.6.1
7.6.2
Datenaustausch mit SQLXML
Diffgrams als Templates 545
Zusammenfassung 548
8
Datenbankgestützte Webanwendungen
8.1
Editierbare Listenansicht einer Datenbanktabelle 551
Hinweise zur Einrichtung von ASP.NET-Projekten 551
Startseite benennen 552
Datenadapter konfigurieren 552
Variante mit Access-Datenbank 552
DataGrid hinzufügen und konfigurieren 554
Seite laden und Daten anzeigen 557
Datenbankzugriff für das Konto ASPNET 559
Paging: Datenausgabe in Ergebnisblöcken 561
Zwischenspeichern des DataSets im Session-Objekt 562
Daten sortieren 564
Datenbindung aus Code neu zuweisen 566
Daten bearbeiten, speichern, löschen 567
Daten hinzufügen 571
Datenbearbeitung über Login schützen 574
8.1.1
8.1.2
8.1.3
8.1.4
8.1.5
8.1.6
8.1.7
8.1.8
8.1.9
8.1.10
8.2
8.2.1
545
8.2.2
8.2.3
8.2.4
Datenbankgestützte Loginseite 574
Einfache Loginprüfung 575
Authentication und Authorization verwenden 576
Loginprüfung mit Datenbankabfrage 577
Login aufrufen 580
Login auswerten 581
A
Links
585
Index
593
549
Inhalt
11
Einleitung
Mit der Neufassung von Visual Studio auf Basis des .NET
Frameworks ist Visual Basic .NET eine echte objektorientierte Sprache geworden, die es in Interaktion mit den
ADO.NET-Datenobjekten sehr leicht macht, asynchrone
Datenbankanwendungen, XML-Webdienste (Web Services) und datenbasierte ASP.NET-Webanwendungen zu
entwickeln.
Der Produktname Visual Basic .NET suggeriert eine Erweiterung oder
einen Versionsschritt der weit verbreiteten Programmiersprache
Visual Basic. Der Zusatz .NET lässt – wie bei anderen Produkten auch
– eine starke Ausrichtung auf das Internet vermuten. Auch ADO.NET
klingt nach einer speziellen Datenzugriffstechnik für vernetzte Lösungen.
Der Titel dieses Buches, Visual Basic .NET und Datenbanken, verspricht
Anleitungen, Grundlageninformationen und Beispielcode zum Datenbankzugriff speziell mit Visual Basic. Wer bereits Visual Basic 6, ADO
und MDAC verwendet hat, wird darin eine Darstellung der neuesten
ActiveX-Datenobjekte und siebzehn neue Techniken zur Verwendung
des Recordsets erwarten.
Um es vorwegzunehmen: Keine dieser Vermutungen trifft wirklich zu,
denn weder ist Visual Basic .NET eine Fortschreibung von Visual Basic
6, noch beschränkt oder konzentriert sich die Verwendung des
namengebenden .NET-Frameworks auf das Internet, und die in diesem Buch beschriebene Datenbanktechnologie ADO.NET wird auch
mit anderen Programmiersprachen verwendet. Und ganz am Rande:
Das Recordset ist abgeschafft.
Die Gemeinsamkeiten von Visual Basic .NET und Visual Basic 6 sind
eine im Wesentlichen übereinstimmende Sprachsyntax, der Name
und das Konzept einer benutzerfreundlichen und produktiven
Arbeitsumgebung. Das ist genug, um sich als Visual Basic 6-Entwicklerin oder -Entwickler schnell zurechtzufinden, und zu wenig, um
ohne Hilfestellungen mit Visual Basic .NET die produktive Arbeit
sofort fortzusetzen.
Einleitung
13
Ein Neubeginn mit Visual Basic .NET
Mit Visual Basic .NET beginnt für VB-Programmierer ein neues Zeitalter, in dem sie sich mit den Basisklassen des umfangreichen .NET
Frameworks auseinandersetzen, die ADO.NET-Datenobjekte kennen
lernen und die Möglichkeiten echter Objektorientierung sehr bald
schätzen lernen werden. Der Neubeginn erfordert es allerdings,
Lösungsansätze und Strategien für Datenbankanwendungen zu überdenken und auf der breiten Basis des .NET Frameworks neu zu konzipieren. Neueinsteiger haben es hier fraglos einfacher, die neue Plattform unbelastet zu entdecken.
Wer eine kontinuierliche Fortsetzung der Geschichte von Visual Basic
erwartet hat, sieht sich vermutlich getäuscht und um den Wert seines
mit Visual Basic 6 erlernten Wissens gebracht. Der Aufwand, Schritt
zu halten, ist nicht zu unterschätzen, denn ohne Basis-Kenntnisse in
objektorientierter Programmierung und ohne ein Verständnis der
Struktur und Funktionsweise des .NET Frameworks dürfte es schwer
sein, erfolgreiche Anwendungen mit Visual Basic .NET zu erstellen.
Die Lösung lautet auch hier: learn it now or be left behind.
Update oder Neuschöpfung?
VB6-Entwickler stehen damit vor dem Problem, dass sich existierender Datenbankcode nur in den seltensten Fällen sinnvoll nach Visual
Basic .NET übertragen lässt. In der Regel wird es angebracht sein, die
datenbankrelevanten Teile der Anwendung ganz neu zu entwickeln,
um von den neuen Eigenschaften zu profitieren.
Einige Anwendungsarten, z.B. jene, die einen ständig aktualisierten
Datenstrom benötigen, oder solche, bei denen ein serverseitiger Cursor erforderlich ist, lassen sich nicht sinnvoll nach ADO.NET übertragen und sollten weiter in VB6 entwickelt und gepflegt werden.
Visual Basic .NET ist eine neue Sprache
Visual Basic .NET stellt kein einfaches Update auf eine neue Version
dar, sondern ist vielmehr eine vollständige, objektorientierte Neufassung einer leicht zu erlernenden Programmiersprache für die .NETPlattform, die – überspitzt formuliert – so entwickelt wurde, dass sich
Visual Basic 6-Programmierer leicht darin zurechtfinden können. Sie
14
Einleitung
werden einerseits große Teile der ihnen gewohnten Syntax wieder finden und sofort anwenden können, andererseits erfordert .NET das
Erlernen neuer Strukturen und neuer Anwendungskonzepte. Die
Unterschiede zur Vorversion sind nicht mehr mit dem Begriff »Neuerungen« zu beschreiben, denn sie greifen tief in die Fundamente
sowohl des Betriebssystems als auch der Programmiersprache(n) und
der Struktur der damit zu erstellenden Anwendungen ein.
Die Rolle der .NET-Plattform
Die Einführung der .NET-Plattform, die u.a. ein umfassendes »Framework« von Basisklassen und Funktionen als Basis aller Programmiersprachen ist, die Microsoft mit dem Visual Studio anbietet, stellt einen
Bruch mit den zuvor in Visual Basic favorisierten Technologien COM
und ActiveX dar. Die .NET-Plattform besteht aus einer sehr großen
Basisklassenbibliothek, einer Sammlung von Compilern für verschiedene Sprachen zur Erzeugung eines »Zwischencodes« und einem
Laufzeitcompiler, der diesen Zwischencode zur Laufzeit in Maschinencode umsetzt. Diese Struktur eröffnet mehrere Möglichkeiten:
Alle Programmiersprachen, die für das .NET Framework verfügbar
sind, benutzen dieselbe Basisklassenbibliothek und haben damit theoretisch die gleichen Möglichkeiten und das gleiche Leistungsbild.
Auch gemischtsprachige Projekte werden damit möglich. Da die
Basisklassenbibliothek einem streng hierarchischen Benennungsschema folgt, kommen für sie erstellte Programme ohne eine Registrierung aus. Statt mit klassischen Installationen können .NET-Anwendungen auch nur durch einfaches Kopieren der Dateien ausgeliefert
werden. Da sie nur das .NET Framework benötigen und erst zur Laufzeit in Code für die vorhandene Hardware umgesetzt werden, werden
.NET-Anwendungen mit dessen (fortgeschrittener) Portierung auf
andere Systeme im Prinzip plattformunabhängig und hardwareunabhängig funktionieren.
Plattformübergreifende Interaktion
Es wäre ein Missverständnis, beim Namen .NET vor allem eine Ausrichtung auf Internetanwendungen zu vermuten. Wesentliche und
direkte Vorteile bringen .NET-Lösungen auch lokal für eine objektorientierte, konsistente Anwendungsentwicklung. Allein schon die überfällige Ablösung von den nur schwer zu wartenden COM-Objekten,
Ein Neubeginn mit Visual Basic .NET
15
der zwingenden Verwendung der Windows-Registry und den Versionisierungsproblemen gemeinsam genutzter Systembibliotheken hätte
den Aufwand einer neuen Programmplattform gerechtfertigt.
Tatsächlich sind aber auch Internetanwendungen und XML-Webdienste, die programmatischen Zugriff auf Logik oder Daten sicher
über das Internet ermöglichen, ein wesentlicher Bestandteil von .NET.
In der Verbindung mit einer durchgehenden Integration von XMLTechnologien lassen sich beliebige Objekte serialisieren und mit dem
SOAP-Protokoll zwischen Anwendungen und Webdiensten übertragen und austauschen.
Bücher zu Visual Basic .NET
Man kann die Bücher, die bereits zu Visual Basic .NET erschienen sind,
in drei Gruppen einteilen. Zur ersten gehören Werke, die den
Umstieg von Visual Basic 6 erleichtern möchten und zeigen, dass man
in Visual Basic .NET auch noch in starker Anlehnung an die Methoden
von Visual Basic 6 programmieren kann. Dabei wird nur soviel von der
Objektorientierung und dem .NET Framework erklärt, wie es unbedingt erforderlich scheint. Zur zweiten Gruppe gehören Grundlagenwerke und zukünftige Referenztitel, in denen objektorientiertes Programmieren von Grund auf erschlossen wird. Diesen Büchern ist
gemeinsam, dass sie, um die Leser auf das Niveau der Anwendungsentwicklung zu führen, auf dem sie sich mit Visual Basic 6 befunden
haben, mindestens den doppelten Umfang eines vergleichbaren Werkes zu Visual Basic 6 haben müssen. In der dritten Gruppe, zu der
auch dieses Buch gehört, werden ausgewählte Sonderthemen behandelt, die wie die Datenbankentwicklung zum Teil dramatischen Veränderungen unterworfen sind. Gleichzeitig spricht diese Gruppe von
Büchern Leserinnen und Leser mit sehr unterschiedlichem Vorwissen
an, so dass es notwendig ist, darin die Darstellung von Grundlagen
und Anwendungskonzepten mit Beispielen und praktischen Hinweisen in ein ausgewogenes Verhältnis zu bringen. Verkürzt gesagt: Ein
einzelnes Buch wird nicht ausreichen, um Visual Basic .NET umfassend zu erschließen. Wenn Sie von Visual Basic 6 umsteigen, empfiehlt sich die Zuhilfenahme eines Werkes aus der zweiten Gruppe.
16
Einleitung
Die neue Datenbanktechnologie ADO.NET
Microsofts Marketingabteilung hat mit dem Namen ADO.NET für die
Datenbanktechnologie in Visual Studio.NET eine zweifelhafte Wahl
getroffen. ADO stand in Visual Basic 6 für ActiveX Data Objects, die
auf der Component Object Model (COM)-Architektur basieren.
ADO.NET hat weder etwas mit ActiveX-Technologie noch mit COM
zu tun und wäre mit seiner Nähe zu XML vielleicht nach William R.
Vaughn ein Kandidat für die Bezeichnung XDO gewesen. Um Verwechslungen zu vermeiden, werden in diesem Buch deshalb ActiveX
Data Objects als ADO(alt), im Gegensatz zu ADO.NET, bezeichnet.
ADO(alt)
Es war einmal ein Recordset
Eine der grundlegenden Änderungen von ADO(alt) zu ADO.NET ist
der Wegfall der universellen Tabelle Recordset zu Gunsten des universellen Datencontainers DataSet, der mehrere Tabellen und ihre Verknüpfungen aufnehmen kann und alle Qualitäten einer transportablen Datenbank hat.
DataSet
Auch wenn das ersatzlose Streichen der zentralen Datenbearbeitungstechnik aus Visual Basic 6 eine drastische Maßnahme ist, die
eine Neuentwicklung aller vorhandenen Datenzugriffsmodule erfordert, lohnt sich der Aufwand, wie Sie bei der Lektüre dieses Buches
sicher feststellen werden.
Das Arbeiten mit DataSets heißt zum einen, dass der gesamte Aufwand entfällt, der notwendig ist, um Daten aus mehreren Datenbanktabellen zuerst in einem Recordset zusammenzufassen, um sie danach
in der Anwendung wieder aufzuschlüsseln und über ein anderes
Recordset gezielt zu aktualisieren. Zum anderen lässt sich ein DataSet
als eine Kopie oder ein Teilauszug der originalen Datenbank in der
Anwendung verstehen. ADO.NET verwendet Hilfsobjekte wie z.B.
Datenadapter, die für den Transport der Daten aus der Datenbank in
das DataSet und zurück sorgen. Folgerichtig ist nur noch dann eine
Verbindung zur Datenbank notwendig, wenn Daten geladen oder
aktualisiert werden.
Die neue Datenbanktechnologie ADO.NET
17
Die Verbindung zur Datenbank
asynchron
Womit die zweite, fundamentale Änderung genannt wäre: ADO.NETAnwendungen verwenden eine Datenbank asynchron, d.h., sie sind
nur noch für den möglichst kurzen Zeitraum, den eine Lese- oder
Schreibaktion dauern sollte, mit der Datenbank verbunden.
In ADO.NET-Anwendungen werden, anders als in ADO(alt), immer
erst alle zu verarbeitenden Daten in das DataSet der Clientanwendung geladen, wo die Datenverarbeitung vollständig getrennt von der
Datenbank stattfindet. Die Entsprechung in ADO(alt) wäre ein clientseitiger Cursor, dem in der Regel zur Steigerung der Leistungsfähigkeit
ein serverseitiger Cursor vorgezogen wird, um möglichst wenig Daten
zum Client zu übertragen und möglichst viel Datenbanklogik vom
Datenbankserver ausführen zu lassen. Mit dem asynchronen Modell
von ADO.NET entfällt diese Option, und der Einsatz der klassischen
serverseitigen Datenverarbeitung reduziert sich auf die Datenbereitstellung und das Ausführen von Gespeicherten Prozeduren.
Ein einmal gefülltes DataSet behält auch, nachdem seine Daten in
einer Anwendung bearbeitet wurden, immer einen Bezug zu seinem
Originalzustand nach dem Laden der Daten. Alle Änderungen, Hinzufügungen und Löschungen werden gekennzeichnet und erlauben es,
zu einem beliebigen späteren Zeitpunkt ausschließlich die Veränderungen in die Datenbank zu übertragen. In diesem Moment lässt sich
sogar feststellen, ob ein anderer Client dieselben Daten ebenfalls verändert hat, um beide Anwender über die Datenkonkurrenz zu informieren.
Ein erstes Beispiel
Im Folgenden soll ein kleines Beispiel einen ersten Eindruck von den
Objekten und Methoden vermitteln, die für einen asynchronen
Datenbankzugriff mit ADO.NET verwendet werden. Dabei werden
mehrere Datenbanktabellen in der Listenansicht eines DataGrids dargestellt.
18
Einleitung
Tabelle laden und darstellen
Das folgende Beispiel finden Sie auf der beiliegenden CD-ROM im
Verzeichnis \Einleitung\dbEinleitung.
Legen Sie in Visual Studio .NET ein Projekt vom Typ WindowsAnwendung an, das Sie dbEinleitung nennen. Ziehen Sie in der Entwurfsansicht der Maske Form1 aus dem Abschnitt Windows Forms der
Toolbox ein DataGrid auf die Maske.
Beispiel
dbEinleitung
Wechseln Sie über das Menü Ansicht • Code in die Code-Ansicht.
Wählen Sie aus dem linken Codenavigations-Dropdown Basisklassenereignisse, dann aus dem rechten Load, um die Ereignisprozedur
für das Laden von Form1 anzulegen, in die der gesamte Code dieses
Beispiels geschrieben wird.
Das Beispiel verwendet die Access-Datenbank dbWürzen.mdb, die Sie
auf der CD-ROM im Verzeichnis \Datenbank finden. Kopieren Sie
diese Datei in das \bin-Unterverzeichnis des Projektverzeichnisses,
das in der Voreinstellung den Pfad %userprofile%\Eigene Dateien\
Visual Studio-Projekte\dbEinleitung hat.
Verbindung zur Datenbank
Die Verbindung zur Datenbank wird über ein Connection-Objekt hergestellt, das dem gleichbenannten Objekt in Visual Basic 6 ähnlich ist
und die gleiche Syntax für die Verbindungszeichenketten verwendet.
Im Beispiel wird Access als OleDB-Datenbank angesprochen, weshalb
die OleDb-Variante des Connection-Objektes erforderlich ist. Da die
Datenbank im Projektausgabeverzeichnis \bin liegt, genügt die
Angabe des Dateinamens in der Verbindungszeichenkette (siehe
Listing 0.1).
Datenadapter
Das Füllen einer Datentabelle aus einer OleDbConnection erfordert
ebenfalls die OleDB-Variante des Datenadapters. Der OleDbDataAdapter wird mit einer SQL-Anweisung für die Datenauswahl und ein
angegebenes Connection-Objekt erstellt. Im Beispiel werden Daten
aus der Tabelle tblGewürze ausgewählt.
Ein erstes Beispiel
19
Datentabelle füllen
Der Vorgang, die Daten zu laden, erfolgt allein durch den Aufruf der
Fill-Methode des Datenadapters, die mit der Datentabelle als Argument aufgerufen wird.
Daten anzeigen
Die Datentabelle, die jetzt Daten enthält, wird dem DataGrid1 als
DataSource-Eigenschaft zugewiesen. Als DataMember genügt der
Name der Datentabelle, den das Objekt DataTable als Textwert trägt.
Listing 0.1 Laden und Darstellen einer Tabelle
Private Sub Form1_Load( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
' Die Verbindung zur Datenbank
Dim _conn As New OleDb.OleDbConnection( _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=dbWürzen.mdb;" & _
"User ID=Admin;")
' Die Datenadapter
Dim _daGewürze As New OleDb.OleDbDataAdapter( _
"SELECT pkGewürz," & _
"
Gewürzname,Gattung " & _
"FROM tblGewürze", _
_conn)
' Eine Datentabelle erzeugen
Dim _dt As New DataTable()
' DataSet über Datenadapter füllen
_daGewürze.Fill(_dt)
_conn.Close()
' DataSet im DataGrid anzeigen
DataGrid1.DataSource= _dt
DataGrid1.DataMember= _dt.ToString
End Sub
Mit dem Start der Anwendung über (F5) wird die Datentabelle über
den Datenadapter gefüllt und im DataGrid angezeigt.
20
Einleitung
Abbildung 0.1 Darstellung einer Datentabelle im DataGrid
Der gezeigte Weg ist nicht aufwändiger als eine funktionsgleiche
Lösung mit einem ADO(alt)-Recordset.
Verknüpfte Tabellen und XML-Daten
In einer erweiterten Version des Beispiels werden mehrere Tabellen in
das DataSet übertragen und dort wie in einer Datenbank verknüpft.
Diese Aufgabe liegt außerhalb der Fähigkeiten einer Recordset-Verarbeitung, ist in ADO.NET jedoch mit einer geringfügigen Erweiterung
des Codes möglich.
Definieren Sie einen zweiten Datenadapter mit einer weiteren SQLAnweisung, im Beispiel stammen die Daten aus der Tabelle tblBilder, die in der Datenbank mit der Tabelle tblGewürze über deren Primärschlüssel verknüpft ist.
Tabellen im DataSet
Ändern Sie den Aufruf der Fill-Methode dahin, dass anstelle der
Datentabelle ein DataSet-Objekt gefüllt wird, das als zusätzliches
Argument einen Namen für die zu erstellende Tabelle erwartet. Fügen
Ein erstes Beispiel
21
Sie den Aufruf eines zweiten Datenadapters hinzu, dessen FillMethode auf das gleiche DataSet angewendet wird.
Verknüpfung
Das DataSet enthält an dieser Stelle zwei benannte Datentabellen, für
die eine Verknüpfung wie in einer Datenbank definiert werden kann.
Es beinhaltet zudem eine Auflistung der Relations genannten Tabellenverknüpfungen, der Sie durch Anwendung der Add-Methode ein
neues DataRelation-Objekt für die beiden Tabellen hinzufügen können. Zur Erzeugung der Tabellenverknüpfung sind lediglich die zu verknüpfenden Spaltennamen, im Beispiel tblGewürze.pkGewürz und
tblBilder.fkGewürz, sowie ein Name für die Verknüpfung erforderlich.
Das DataGrid akzeptiert auch ein DataSet als DataSource-Eigenschaft, als DataMember wird einer der beiden Tabellennamen angegeben, die aus der Tables-Auflistung des DataSets verfügbar sind.
XML-Daten
Um die Verknüpfung der Daten in der Anzeige zu sehen, kann ein
Textbox-Steuerelement verwendet werden, in dem die Daten des
DataSets als XML-Daten ausgegeben werden. Ziehen Sie dazu in der
Entwurfsansicht von Form1 eine Textbox aus der Toolbar auf die
Maske. Setzen Sie im Eigenschaftenfenster die Eigenschaft Multiline
auf True und positionieren Sie die Textbox1 neben dem DataGrid1
auf der Maske.
Die XML-Daten werden in einem Schritt mit Hilfe der GetXmlMethode des DataSets in die Textbox übertragen.
Fügen Sie vor der Anzeige der XML-Daten eine Zuweisung des Wertes
True an die Nested-Eigenschaft der DataRelation im DataSet ein, um
eine geschachtelte Darstellung der XML-Daten zu erhalten.
Listing 0.2 Tabellen verknüpfen und XML-Daten anzeigen
Private Sub Form1_Load( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
22
Einleitung
' Die Verbindung zur Datenbank
Dim _conn As New OleDb.OleDbConnection( _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=dbWürzen.mdb;" & _
"User ID=Admin;")
' Die Datenadapter
Dim _daGewürze As New OleDb.OleDbDataAdapter( _
"SELECT pkGewürz," & _
"
Gewürzname,Gattung " & _
"FROM tblGewürze", _
_conn)
Dim _daBilder As New OleDb.OleDbDataAdapter( _
"SELECT fkGewürz," & _
"
Bildname " & _
"FROM tblBilder", _
_conn)
' Das DataSet erzeugen
Dim _ds As New DataSet()
' DataSet über Datenadapter füllen
_daGewürze.Fill(_ds, "Gewürze")
_daBilder.Fill(_ds, "Bilder")
_conn.Close()
' Tabellenbeziehung einrichten
_ds.Relations.Add( _
New DataRelation( _
"relGewürzeBilder", _
_ds.Tables("Gewürze").Columns("pkGewürz"), _
_ds.Tables("Bilder").Columns("fkGewürz")))
' Tabellenbeziehung geschachtelt darstellen
_ds.Relations.Item(0).Nested = True
' DataSet im DataGrid anzeigen
DataGrid1.DataSource= _ds
DataGrid1.DataMember= _ds.Tables("Gewürze").ToString
' DataSet als XML anzeigen
TextBox1.Text = _ds.GetXml
End Sub
Starten Sie die Anwendung erneut. Sie sehen neben der Datentabelle
Gewürze in der Textbox den XML-Code des dazugehörigen DataSets
angezeigt, der in jedem Abschnitt Gewürze den entsprechenden
Abschnitt aus Bilder enthält.
Ein erstes Beispiel
23
In der Tabellenansicht sehen Sie im Kopf jeder Zeile einen Datenknoten, der beim Klick einen Link auf die in der Tabelle Bilder verknüpften
Daten freigibt. Folgen Sie diesem Link, wird der entsprechende
Datensatz der Tabelle Bilder im DataGrid angezeigt.
Abbildung 0.2 Darstellung verknüpfter Tabellen im DataGrid und als XML-Daten-
menge
Der Aufwand, der in diesem Beispiel notwendig war, um Tabellen zu
übertragen, sie zu verknüpfen und in verschiedenen Modi auszugeben, ist minimal und erfordert keinerlei Datenschleifen und keine
direkte Auswertung der Tabelleninhalte.
Die gesamte Darstellung der Inhalte findet statt, nachdem die Verbindung zur Datenbank schon wieder geschlossen ist.
Nach einer Veränderung der Daten im DataSet könnte ein Aktualisieren der Datenbank, für jede Tabelle getrennt, allein über den Aufruf
der Update-Methode des Datenadapters erfolgen.
24
Einleitung
Dieses Buch
Dieses Buch ist in der Absicht geschrieben, den Einstieg in die Datenbankprogrammierung mit Visual Basic .NET zu erleichtern und über
die Hürden und Fallstricke hinweg zu helfen, die der Autor bei der
Beschäftigung mit Visual Studio .NET seit der Beta 1 selbst erfahren
hat. Anhand von praktischen Beispielen und wieder verwendbarem
Code soll es Sie in die Programmierung von Datenbankanwendungen
mit Visual Basic .NET einführen.
Sie lernen die Leistungsfähigkeit und Vorteile der neuen Datenbankobjekte in ADO.NET und ihre Bedeutung für neue Anwendungskonzepte kennen und erfahren Konzepte und Techniken, wie Sie mit
Visual Basic .NET Datenbanken nutzen, erstellen, ändern, Daten darstellen, bearbeiten und austauschen.
Kapitel 1 stellt die neue Entwicklungsumgebung von Visual Studio
.NET vor.
왘
Es werden Hinweise zur Installation und Einrichtung von Visual
Studio .NET gegeben sowie einige zusätzliche Datenprovider für
ADO.NET vorgestellt, die durch Download ergänzt werden können.
왘
Es gibt einen Überblick über die wichtigsten Bestandteile und
Funktionen zum Erstellen, Debuggen, Verwalten und Ausliefern
von Anwendungen für das .NET Framework.
왘
Die Einrichtung der Desktop-Version von SQL Server 2000 (MSDE)
und des IIS-Webservers wird beschrieben sowie die manuelle
Installation der Beispieldatenbank dbWürzen auf einem SQL Server
und in einer MySQL-Datenbank.
Kapitel 2 stellt das neue Visual Basic in den größeren Zusammenhang
des .NET Frameworks und erklärt seine Bestandteile.
왘
Sie erfahren, was die Common Language Runtime und was eine
Assembly ist, wie Visual Basic .NET mit dem .NET Framework interagiert und wie Komponenten versioniert werden.
왘
Es werden Hinweise zum Sicherheitskonzept und der Speicherverwaltung des .NET Frameworks gegeben.
Dieses Buch
25
Kapitel 3 zeigt den Zusammenhang von .NET Framework, XML-Webdiensten (Web Services) und dem programmierbaren Internet in der
.NET-Strategie von Microsoft, um die Hintergründe der grundlegenden Neuerungen deutlich zu machen.
왘
Es erläutert, warum Microsoft entschieden hat, die COM-Technologie nicht weiter zu verfolgen und stattdessen das .NET Framework als Grundlage zukünftiger Windows-Versionen zu verwenden.
왘
Es wird gezeigt, was es mit dem Schlagwort vom programmierbaren
Internet auf sich hat und welche Rolle XML-Webdienste in diesem
Konzept spielen.
왘
Sie erfahren, wie mit Hilfe der Common Language Infrastructure
.NET-Code auch auf anderen Plattformen kompiliert und ausgeführt werden kann und welche Initiativen es dazu gibt.
Kapitel 4 stellt Grundlagen der Datenbankbearbeitung mit relationalen Datenbanken und hierarchischen Datenmengen in XML dar.
왘
Es wird der Entwurf relationaler Datenbanken unter der Berücksichtigung der klassischen Normalformen gezeigt.
왘
Ein eigener Abschnitt führt anhand von Beispielen in die Grundlagen von SQL-Anweisungen ein.
왘
Da mit ADO.NET neben dem klassischen SQL auch XML-Technologien bei der Entwicklung datenbankgestützter Anwendungen zum
Einsatz kommen, wird erläutert, wie XML-Daten und XML-Datenschemas aufgebaut sind. Ihre Verarbeitung wird anhand der XPathNavigation und mit XQuery-Abfragen demonstriert.
왘
Die Darstellung der Grundlagen wird ergänzt durch Anleitungen
zur Verwaltung und Administration von SQL Server-Datenbanken
mit dem SQL Server Enterprise Manager und über SQL-Skripte.
Kapitel 5 erklärt das objektbasierte Datenbankmodell von ADO.NET
ausführlich anhand praxisnaher Aufgabenstellungen und Themen.
왘
26
Es wird ein Überblick über die Objekte gegeben, die in ADO.NET
verwendet werden, und ihr Zusammenhang an einem vollständigen Beispiel einer datengebundenen Maske erläutert.
Einleitung
왘
Das neue und zentrale Datenbankobjekt DataSet wird ausführlich
erläutert, und seine Unterobjekte werden einzeln vorgestellt. Die
Objekte werden durch Auflistungen ihrer Eigenschaften und
Methoden direkt vergleichbar gemacht, um sie in Anwendungen
gezielt einsetzen zu können.
왘
Es werden fortgeschrittene Themen wie die Darstellung hierarchischer Daten und die Verarbeitung von Bildern in der Datenbank
behandelt.
왘
Es geht auch um die Aufgaben zur Aktualisierung und zur Behandlung von Datenkonkurrenz, die sich mit dem asynchronen Modell
von ADO.NET neu stellen.
Kapitel 6 stellt Codemodule vor, mit deren Hilfe Sie den Datenzugriff
in ADO.NET-Anwendungen vereinfachen und vereinheitlichen können.
왘
Es erläutert Hilfsanwendungen zur Analyse von Datenbanktabellen
und von Gespeicherten Prozeduren.
왘
Es gibt eine Einführung in den Data Access Application Block, mit
dem Microsoft optimierten Datenzugriffscode als .NET-Komponente veröffentlicht hat.
Kapitel 7 behandelt die serverseitige Datenverarbeitung verteilter
Komponenten.
왘
Es wird gezeigt, wie ASP.NET-Webdienste zur Anzeige und zur
Aktualisierung von Datenbankinhalten erstellt und genutzt werden.
왘
Es behandelt die XML-Verarbeitung im SQL Server 2000 und die
Veröffentlichung von Webdiensten aus dem SQL Server.
왘
Der Datenaustausch zwischen den Ebenen einer Anwendung wird
erläutert und das dabei verwendete Diffgram-Format des DataSets
dargestellt.
Dieses Buch
27
Kapitel 8 beschäftigt sich mit datenbankgestützten Webanwendungen.
왘
Es führt in die Anwendung der ADO.NET-Programmierung auf
ASP.NET-Webanwendungen ein und gibt Hinweise zur Umsetzung
von Webprojekten und verteilten Anwendungen.
Das Buch ist somit in mehrere Teile untergliedert, die unterschiedlichen Leserbedürfnissen bzw. -perspektiven Rechnung tragen. Die
Kapitel 1 bis 3 eignen sich als Einführung und Überblick über die Technologie und ermöglichen damit eine Einschätzung des Aufwands, von
Visual Basic 6 auf Visual Basic .NET umzustellen. Kapitel 4 und 5 sind
die Lernkapitel für Programmierer, in denen die Datenbankgrundlagen und die neue Datenbanktechnologie ADO.NET an vielen Beispielen vermittelt werden. Kapitel 6 vertieft die Thematik für fortgeschrittene Anwender, während Kapitel 7 und 8 erweiterte Projekte und
verteilte Anwendungen darstellen.
Systemvoraussetzungen und Installation
Um die Beispiele in diesem Buch nachvollziehen zu können, muss
mindestens das .NET Framework, besser natürlich Visual Studio .NET
installiert werden. Da das .NET Framework den Compiler für Visual
Basic .NET vbc.exe enthält, können damit vollständige Anwendungen
allein aus dem Quellcode und den Beispielen dieses Buches kompiliert werden. Die Beispielprojekte in diesem Buch basieren auf dem
Funktionsumfang der Professional-Ausgabe von Visual Studio .NET
und funktionieren mit dem .NET Framework 1.0 mit Servicepack 2.
Sie finden das .NET Framework und das .NET Framework SDK sowie
das aktuelle Servicepack auf der beiliegenden CD-ROM im Verzeichnis \DotNETFramework.
Bei der Installation von Visual Studio .NET oder des .NET Framework
SDK haben Sie die Option, eine Desktopversion MSDE des SQL Server 2000 einzurichten, die als Datenbankserver für die Beispiele in
diesem Buch völlig ausreicht.
Bitte beachten Sie, dass die ausführbaren Anwendungsdateien der
Beispielprojekte, die sich im Projektausgabeverzeichnis \bin einer Beispielanwendung befinden, bezüglich der installierten Version des
.NET Frameworks versionsgebunden sind. Sie müssen nach Anwen-
28
Einleitung
dung eines neueren Servicepacks oder einer Aktualisierung des
Frameworks neu kompiliert werden.
Die Beispiele dieses Buches
Die Beispieldatenbank für die Beispiele in diesem Buch heißt dbWürzen und enthält eine Sammlung von Gewürzen mit Informationen zur
Gattung, Herkunft und den Mischungen, in denen sie verwendet werden. Die Datenbank ist sowohl als SQL Server-Datenbank dbWürzen
als auch als Access-Datenbank dbWürzen.mdb und als allgemeines
SQL-Datenbankskript auf der CD-ROM im Verzeichnis \Datenbank zu
finden. Die Einrichtung der Datenbank per Datenbankskript wird in
Abschnitt 1.9, »Installieren der Beispieldatenbank«, beschrieben. Die
meisten Beispiele in diesem Buch funktionieren sowohl mit der
Access- als auch mit der SQL Server-Datenbank. Einige Beispiele sind
jedoch ausschließlich mit einer SQL Server-Datenbank zu nutzen.
Datenbank
dbWürzen
Auf der beiliegenden CD-ROM finden Sie im Verzeichnis \Installation
drei Installationsprogramme, mit denen die Beispieldatenbank
dbWürzen und die Beispiele dieses Buches, getrennt nach Windowsund Webanwendungen, einschließlich Quellcode installiert und bis
auf die Datenbank auch wieder deinstalliert werden können.
왘
dbVBNETAnwendungen.msi
Installiert die Windows-Beispielanwendungen aus den Kapiteln 1,
4, 5 und 6.
Die Installation setzt voraus, dass zuvor Visual Studio .NET oder das
.NET Framework sowie ein SQL Server oder eine MSDE installiert wurden. Der Datenbankserver sollte zum Zeitpunkt der Installation bereits
laufen.
왘
dbVBNETWebAnwendungen.msi
Installiert die Webdienste und Webanwendungen aus den Kapiteln
7 und 8.
Die Installation setzt voraus, dass Sie einen lokalen IIS-Webserver und
Visual Studio .NET oder das .NET Framework installiert haben. Das
Installationsprogramm legt virtuelle Verzeichnisse für die Webdienstbeispiele aus Kapitel 7 und die Webanwendungsbeispiele aus Kapitel 8
an.
Dieses Buch
29
왘
dbWürzenMSSQL.msi
Installiert die SQL Server-Datenbank dbWürzen, die in den Beispielen verwendet wird.
Während der Installation werden Sie nach dem Namen eines SQL Servers gefragt, auf den Sie Zugriff mit Integrierter Sicherheit haben. Der
Servername eines SQL Servers auf einem Einzelrechner ist entweder
(local) oder, wenn Sie eine MSDE benutzen, (local)\NetSDK oder
(local)\VSdotNET.
Startmenü
Die Installationsprogramme fügen dem Startmenü unter Programme
den Punkt VB.NET und Datenbanken hinzu, der nach Kapiteln sortierte Verknüpfungen zu den Visual Studio .NET-Beispielprojekten
und zu den ausführbaren fertigen Beispielanwendungen enthält.
Beispielcode
Auf der beiliegenden CD-ROM finden Sie in den mit Kapitelnamen
benannten Unterverzeichnissen die beschriebenen Beispielprojekte
und alle Listings aus den Anleitungen innerhalb der einzelnen Kapitel.
Die Beispielprojekte können auch ohne die Verwendung des Installationsprogrammes durch Kopieren der entsprechenden Verzeichnisse
von der beiliegenden CD-ROM verwendet werden. Der dabei übertragene Schreibschutz sollte zurückgesetzt werden. Die Projekte sind
vollständig und enthalten im Unterverzeichnis \bin jeweils die ausführbare *.exe-Datei, sofern es sich nicht um eines der Webprojekte
handelt.
Von der Startseite index.html der beiliegenden CD-ROM aus haben
Sie direkten Zugriff auf alle Beispieldateien und Programmlistings.
Eventuell notwendige Aktualisierungen finden Sie mit Ihrer persönlichen Registriernummer am Ende des Buches auf der Website von
Galileo Press unter: http://www.galileocomputing.de.
Konventionen und Symbole
Programmcode und Listings
Soweit es zum Verständnis notwendig ist, wird im Text der dazugehörige Programmcode dargestellt. An den Stellen, an denen satztechnisch ein Umbruch erforderlich ist, werden die Zeilenfortsetzungszeichen _ und & _ eingesetzt, die auch von Visual Basic .NET verwendet
werden. Damit ist es unproblematisch, den Programmcode auch
manuell zu übertragen.
30
Einleitung
Abbildung 0.3 Zugriff auf Beispiele und Listings auf der CD-ROM dieses Buches
Konventionen zur Objektbenennung
In diesem Buch werden Objekte in Anlehnung an die Konventionen
benannt, die Greg Reddick für Visual Basic-Programme aufgestellt hat.
Einen Link zu den Reddick VBA Naming Conventions (RVBA) finden Sie
in der Linkliste im Anhang. An derselben Stelle gibt es auch einen Verweis auf die Arbeitsversion der Reddick .NET Conventions für VB.NETObjekte.
In der Absicht, Programme leichter lesbar zu machen, ermöglichen es
diese Benennungsregeln, Objektnamen so aufzubauen, dass daran
Informationen über die Art, den Geltungsbereich und die Herkunft
eines Objektes ablesbar werden.
Die Regeln folgen der so genannten Ungarischen Notation, nach der
Objekte einheitlich benannt werden in der Form:
[Präfix]Tag[BasisObjektName[Suffix]]
Die BasisObjektNamen werden wie in Pascal mit großen Buchstaben
am Anfang jeden Wortes geschrieben, wie z.B. in GewürzListe.
Dieses Buch
31
Ergänzt werden diese Namen durch einen kurzen, vorangestellten
Tag, der den Datentyp des Objektes angeben sollte, z.B. strGewürzName für eine Zeichenkette (String) oder intGewürz für einen IntegerWert.
Die aus Tag und BasisObjektName zusammengesetzte Benennung
erhält einen Präfix, der den Geltungsbereich des Objektes angibt:
왘
g für öffentliche, als public deklarierte, globale Variablen, z.B.
gintGewürze
왘
m für modulweite, als private deklarierte, gültige Variablen, z.B.
mstrGattung
왘
s für als static deklarierte Variablen, z.B. sintAufrufe
Ohne Präfix bleiben nur prozedurweit gültige Objekte.
In diesem Buch werden allgemeine, in ihrem Geltungsbereich auf Prozeduren beschränkte Objekte in der Regel nur mit dem Tag und dem
Präfix _ benannt, z.B. _dt für eine Datentabelle.
Geltungsbereich von Variablen
Mit Visual Basic .NET werden neue, auf Prozedurabschnitte begrenzte
Geltungsbereiche von Variablen eingeführt. Eine innerhalb einer
Schleife oder einer strukturierten Fehlerbehandlung deklarierte Variable ist nur dort gültig.
Dim arrNamen as String()
Dim _str as String
For each _str in arrNamen
Dim _int as Integer
_int = _str.Length
Console.writeLine(_int)
Next
Die verborgene Variable _int belegt nur für die Dauer der Schleifenauswertung Ressourcen und steht außerhalb der Schleife nicht zur
Verfügung. Dort kann allerdings auch keine weitere Variable mit dem
Namen _int deklariert werden. Auf die fehlerhafte Verwendung
verborgener Variablen werden Sie bereits zur Entwurfszeit von Visual
Studio hingewiesen.
32
Einleitung
Symbole
Tipp
In den Hauptkapiteln finden Sie am Ende wichtiger Abschnitte grau
unterlegte Anleitungen, in denen praktische Hinweise und Anleitungen zur Konfiguration und Einrichtung von Visual Studio .NET
und den verwendeten Serverdiensten gegeben werden.
Hinweise warnen vor bekannten Missverständnissen und sollen der
Vermeidung häufig vorkommender Probleme dienen. Ebenso kann es
sich um Verweise auf weiterführende Informationen handeln.
Dieses Buch
33
' Einzelnen Datensatz anzeigen
MessageBox.Show(dtGewürze.Rows.Item(12). _
Item("Gewürzname").ToString)
End Sub
Das XMLDocument enthält eine Eigenschaft DataSet, die eine relationale Darstellung seiner Inhalte ermöglicht. XML-Daten werden im
XML-Dokument mit Hilfe ihres abgeleiteten Schemas ausgewertet,
und können über das relationale Modell des DataSets z.B. als Datenzeilen angesprochen werden. Im Beispiel wird der Inhalt des DataSets erneut in XML gewandelt und in derTextbox angezeigt.
Abbildung 5.29 XML-Daten aus einem synchronisierten DataSet
5.7
Datenobjekte des DataSets
5.7.1
DataTable
Die DataTable ist ein einfaches Objekt, das eine Datenquelle darstellt. Es enthält keine Information über die Herkunft der Daten, mit
denen es gefüllt ist. Eine DataTable kann alleine oder innerhalb eines
DataSets verwendet werden.
Datenobjekte des DataSets
343
Auslesen von
Änderungen über
GetChanges()
DataTables überwachen wie DataSets Änderungen an den geladenen Daten bzw. sind Bestandteil der Änderungsverfolgung von DataSets. Geänderte Daten können über die Methode GetChanges als
eigene DataTable ausgelesen werden. Dieser Mechanismus wird beispielsweise über einen DataAdapter ausgelöst, um separate Aktionen
für das Aktualisieren von Daten zu definieren.
Eine DataTable kann manuell erzeugt werden, wird aber auch durch
die Fill-Methode des DataAdapters in einem DataSet automatisch
erstellt und gefüllt. Sie dient der Bearbeitung, Navigation, Sortierung
und Filterung von Daten im Speicher sowie der Erstellung von Sichten.
DataTable programmatisch erzeugen
Es ist sehr einfach, programmatisch eine DataTable anzulegen und in
ein DataSet einzufügen.
Beispiel dtKlimata
Das folgende Beispiel ist ein Projekt vom Typ Konsolenanwendung,
das ohne Maske und ohne Datenbankverbindung auskommt, da ein
DataSet und eine DataTable programmatisch erstellt werden. Sie finden das Beispielprojekt auf der beiliegenden CD-ROM im Verzeichnis
\Kapitel05\dtKlimata.
In diesem Beispiel wird eine Datentabelle dtKlimata mit drei Datenspalten pkKlima, Klimaname und Temperatur angelegt und in ein
DataSet eingefügt. Daten und Schema des DataSets werden anschließend als XML-Daten im Anwendungsverzeichnis ausgegeben.
Listing 5.29 Programmatisch erzeugte DataTable im DataSet
Dim dsDataSet As DataSet
Dim dtKlimata As DataTable
Sub Main()
' DataSet erzeugen
Dim dsDataSet As DataSet
dsDataSet = New DataSet()
' DataTable erzeugen
Dim dtKlimata As DataTable
dtKlimata = New DataTable("tblKlimata")
' Tabellenspalte mit Primärschlüssel definieren
Dim dcNeu As DataColumn
dcNeu = New DataColumn()
344
ADO.NET und Datenbanken
dcNeu.ColumnName = "pkKlima"
dcNeu.DataType = Type.GetType("System.Int32")
' Primärschlüsselwerte automatisch erzeugen
dcNeu.AutoIncrement = True
dcNeu.AutoIncrementSeed = 1
dcNeu.AutoIncrementStep = 1
dcNeu.AllowDBNull = False
' Tabellenspalte in die Tabelle einfügen
dtKlimata.Columns.Add(dcNeu)
' Tabellenspalte einem Primärschlüssel-Array
' hinzufügen
Dim arrPrimaryKey(0) As DataColumn
arrPrimaryKey(0) = dcNeu
' Primärschlüssel in der Tabelle setzen
dtKlimata.PrimaryKey = arrPrimaryKey
' Tabellenspalte für den Namen definieren
dcNeu = New DataColumn()
dcNeu.ColumnName = "Klimaname"
dcNeu.DataType = Type.GetType("System.String")
' Tabellenspalte in die Tabelle einfügen
dtKlimata.Columns.Add(dcNeu)
' Tabellenspalte für die Beschreibung definieren
dcNeu = New DataColumn()
dcNeu.ColumnName = "Temperatur"
dcNeu.DataType = Type.GetType("System.Int32")
' Tabellenspalte in die Tabelle einfügen
dtKlimata.Columns.Add(dcNeu)
'Tabelle dem DataSet hinzufügen
dsDataSet.Tables.Add(dtKlimata)
' DataSet und Schema als XML speichern
dsDataSet.WriteXmlSchema("dtKlimataSchema.xml")
dsDataSet.WriteXml("dtKlimata.xml")
End Sub
Zunächst wird eine neue Datentabelle tblKlimata erzeugt, der eine
Spalte pkKlima hinzugefügt wird, die Primärschlüssel werden soll.
Jede Spalte, die den Primärschlüssel aufnehmen soll, muss gleichzeitig
als Array von Datenspalten angelegt werden, das die Datentabelle
über die Eigenschaft PrimaryKey aufnimmt. Außer der Schlüsselspalte
werden noch die Datenspalten Klimaname und Temperatur vom Typ
Zeichenkette und Integer hinzugefügt.
Datenobjekte des DataSets
PrimärschlüsselArray
345
Um die neue DataTable auszuwerten, wird sie in ein neues DataSet
eingefügt, dessen Schema und Inhalte anschließend im XML-Format
ausgegeben werden.
Testen Sie das Beispiel und öffnen Sie danach die Datei dtKlimataSchema.xml im Projektausgabeverzeichnis \bin.
Abbildung 5.30 Schema der DataTable dtKlimata im DataSet
Das Schema enthält die Spaltendefinitionen und den Primärschlüssel
der programmatisch erzeugten Datentabelle dtKlimata.
DataTable-Eigenschaften
Das Objekt DataTable hat folgende Eigenschaften:
Name
Beschreibung
CaseSensitive
Setzt oder liest einen Booleschen Wert, der festlegt, ob ein
Zeichenkettenvergleich schreibungssensitiv ausgeführt
wird. Wenn die Tabelle Teil eines DataSets ist, übernimmt
sie diese Eigenschaft vom DataSet. Bei der programmatischen Erzeugung einer DataTable wird dieser Wert mit
False vorgegeben.
Tabelle 5.16 DataTable-Eigenschaften
346
ADO.NET und Datenbanken
Name
Beschreibung
ChildRelations
Gibt alle Detaildatenbeziehungen der Tabelle als Auflistung
vom Typ DataRelationCollection zurück oder
Nothing, wenn keine Detaildatenbeziehungen vorhanden
sind
Columns
Gibt eine Auflistung der Spalten der Tabelle als Auflistung
vom Typ DataColumnCollection zurück oder Nothing,
wenn keine Spalten vorhanden sind
Constraints
Gibt eine Auflistung der Einschränkungen der Tabelle als
Auflistung vom Typ ConstraintCollection zurück oder
Nothing, wenn keine Einschränkungen vorhanden sind
DataSet
Gibt das DataSet, zu dem die Tabelle gehört, als Objekt
zurück
DefaultView
Gibt ein DataView-Objekt zurück, das eine benutzerdefinierte, sortierte und gefilterte Sicht auf die Tabelle oder
einen einzelnen Datensatz sein kann
DisplayExpression
Setzt oder liest eine Zeichenkette, die benutzt werden
kann, die Tabelle in der Benutzeroberfläche zu kennzeichnen
ExtendedProper- Gibt eine Auflistung der benutzerdefinierten Informationen
als PropertyCollection zurück. Die Eigenschaft besitzt
ties
eine Methode Add, mit der ExtendedProperties als Zeichenketten hinzugefügt werden können: dtDataTable.ExtendedProperties.Add("Eingabe","Testdaten").
HasErrors
Gibt einen Booleschen Wert zurück, der angibt, ob in einer
der Datenzeilen der DataTable nach einer Aktion Fehler
aufgetreten sind
Locale
Setzt oder liest die Lokalisierungsinformation, die für Zeichenkettenvergleiche in der DataTable ausgewertet wird
MinimumCapacity Setzt oder liest die Größe, die DataTable zu Beginn hatte
Namespace
Setzt oder liest den Namensraum der XML-Repräsentation
der Daten in dieser Tabelle
ParentRelations Gibt die Auflistung von übergeordneten Beziehungen für
diese Tabelle zurück oder Nothing, wenn keine existiert
Prefix
Setzt oder liest das Namensraum-Präfix der XML-Repräsentation der Daten in der DataTable
PrimaryKey
Setzt oder liest ein Array von DataColumn-Objekten, das
die Primärschlüssel für die DataTable darstellt
Tabelle 5.16 DataTable-Eigenschaften (Forts.)
Datenobjekte des DataSets
347
Name
Beschreibung
Rows
Gibt die Auflistung von DataRows zurück, die zu der DataTable gehören, oder Nothing, wenn keine DataRow vorhanden ist
TableName
Setzt oder liest den Namen der Tabelle. Der Name wird
auch über die DataTableCollection des DataSets ausgewertet.
Tabelle 5.16 DataTable-Eigenschaften (Forts.)
DataTable-Methoden
Eine DataTable stellt die folgenden Methoden zur Verfügung:
Name
Beschreibung
AcceptChanges() Übernimmt alle Änderungen, die an der DataTable seit
dem letzten Aufruf dieser Methode oder seit dem Laden
der DataTable vorgenommen wurden. Ändert den
Zustand aller geänderten Zeilen auf unchanged und ist
daher erst nach dem Aufruf der Update-Methode sinnvoll
BeginInit()
Mit BeginInit kann gesteuert werden, wann eine DataTable initialisiert wird, die auf einer Maske oder in einer
Komponente verwendet werden soll. Nach Aufruf von
BeginInit wird zur Laufzeit auf den Aufruf von EndInit
gewartet, um sicherzustellen, dass die DataTable nicht
verwendet wird, bevor sie vollständig initialisiert wurde.
BeginLoadData() Wird im Zusammenhang mit EndLoadData verwendet.
Diese Methode deaktiviert Benachrichtigungen, IndexAktualisierungen und Einschränkungen, während Daten
geladen werden.
Clear()
Löscht alle Daten aus der DataTable
Clone()
Kopiert die Struktur der DataTable inklusive des Schemas
der DataTable und ihrer Einschränkungen
Compute()
Erwartet zwei Argumente als Zeichenketten. Wendet eine
der beiden Aggregatfunktionen SUM oder COUNT, die als
erstes Argument angegeben ist, auf die mit der im zweiten
Argument angegebenen Bedingung gefilterten aktuellen
Datenzeilen an.
Copy()
Kopiert die Struktur und Daten der DataTable
Tabelle 5.17 DataTable-Methoden
348
ADO.NET und Datenbanken
Name
Beschreibung
EndInit()
Beendet die Initialisierung der DataTable in Verbindung
mit BeginInit() zur Laufzeit und gibt sie zur Verwendung frei
EndLoadData()
Wird im Zusammenhang mit BeginLoadData verwendet.
Diese Methode aktiviert Benachrichtigungen, Index-Aktualisierungen und Einschränkungen, nachdem Daten geladen wurden.
GetChanges()
Gibt eine Kopie der DataTable zurück, die alle Datenzeilen enthält, die seit dem Laden der DataTable oder dem
letzten Aufruf von AcceptChanges geändert worden sind.
GetErrors()
Gibt ein Array von DataRows zurück, in denen ein Fehler
aufgetreten ist
ImportRow()
Kopiert eine DataRow in eine DataTable, wobei alle Eigenschaften sowie der Originalinhalt und der aktuelle, möglicherweise geänderte Wert erhalten bleiben
LoadDataRow()
Sucht und aktualisiert eine bestimmte Datenzeile. Wenn
diese nicht gefunden werden kann, wird sie mit den angegebenen Werten erstellt.
NewRow()
Erzeugt eine neue DataRow entsprechend dem Tabellenschema
RejectChanges() Macht alle Änderungen, die seit dem Laden der DataTable oder dem letzten Aufruf von AcceptChanges
gemacht wurden, rückgängig
Reset()
Versetzt die DataTable in ihren Originalzustand zurück
Select()
Gibt ein Array von DataRows zurück, das nach dem
Primärschlüssel, sofern vorhanden, sortiert ist
Tabelle 5.17 DataTable-Methoden (Forts.)
Ereignisse der DataTable
Datentabellen stellen Ereignisse zur Verfügung, mit denen Veränderungen an Daten in Spalten und Zeilen gehandhabt werden können.
Die DataTable kann sechs Ereignisse auslösen:
Datenobjekte des DataSets
349
Ereignis
Beschreibung
ColumnChanged
Tritt ein, wenn ein Wert in eine Spalte erfolgreich eingefügt werden konnte
ColumnChanging
Tritt ein, wenn ein Wert in eine Spalte eingetragen wird
RowChanged
Tritt ein, nachdem eine Datenzeile erfolgreich aktualisiert
wurde
RowChanging
Tritt ein, wenn eine Datenzeile in der Tabelle verändert
wird
RowDeleted
Tritt ein, wenn eine Datenzeile in der Tabelle als gelöscht
markiert wurde
RowDeleting
Tritt ein, bevor eine Datenzeile in der Tabelle als gelöscht
markiert wird
Tabelle 5.18 DataTable-Ereignisse
Die Ereignisse der DataTable sind paarweise angelegt, beispielsweise
tritt RowChanging während der Änderung einer Datenzeile auf, RowChanged, wenn sie erfolgreich geändert wurde.
GetErrors nach
RowChanging
auswerten
Für eine Anwendung mit datengebundenen Eingabefeldern stellen
DataTable-Ereignisse eine gute Möglichkeit dar, eingegebene Daten
zu überprüfen. Problematische Datensätze können über die GetErrors-Methode der DataTable entdeckt werden, nachdem das
RowChanging-Ereignis ausgelöst worden ist.
In der Fortsetzung des vorausgegangenen Beispiels sollen die Datenzeilen der DataTable programmatisch geändert werden. In einer Prozedur DataTable_Füllen werden zunächst drei Tabellenzeilen
erzeugt, die mit Hilfe der Prozedur DataTable_Ändern geändert werden, um das Änderungsereignis auszulösen.
Listing 5.30 Prozedur zum Füllen der Tabelle
Private Sub DataTable_Füllen()
Dim i As Integer
Dim drInhalt As DataRow
For i = 1 To 3
drInhalt = dtKlimata.NewRow
drInhalt("Klimaname") = "Name_" + i.ToString
drInhalt("Temperatur") = Cint(Rnd(i) * 30)
dtKlimata.Rows.Add(drInhalt)
350
ADO.NET und Datenbanken
Next
End Sub
Listing 5.31 Prozedur zum Ändern einer Datenzeile
Private Sub DataTable_Ändern()
dtKlimata.Rows.Item(1)("Beschreibung") = 20
End Sub
Ereignisprozeduren
Um ein Ereignis auszuwerten, muss zuerst eine entsprechende Ereignisprozedur angelegt werden.
Listing 5.32 Ereignisprozedur für die Spaltenänderung
Private Sub SpaltenÄnderung( _
ByVal sender As Object, _
ByVal e As DataColumnChangeEventArgs)
MsgBox("In Datenzeile: " & _
e.Row("Klimaname").ToString & _
" soll die Spalte " & e.Column.ColumnName & _
" in: " + CStr(e.ProposedValue) & _
" geändert werden.")
End Sub
Zum Aufruf der Ereignistests wird die Prozedur Main ergänzt und
darin das Füllen der Datenzeilen nach dem Einsetzen der Tabelle in
das DataSet aufgerufen. Danach wird die Ereignisprozedur der Zeilenänderung an den Ereignishandler der Tabelle gebunden und anschließend die Änderung ausgelöst.
Listing 5.33 Auslösen des Änderungsereignisses, Auszug aus erweiterter Prozedur Sub
Main()
' ...
dsDataSet.Tables.Add(dtKlimata)
' Tabelle mit einigen Zeilen füllen
DataTable_Füllen()
' Ereignisbehandlung an DataTable binden
AddHandler dtKlimata.ColumnChanging, _
New DataColumnChangeEventHandler( _
AddressOf SpaltenÄnderung)
Datenobjekte des DataSets
351
' Tabelle ändern
DataTable_Ändern()
' Fortsetzung Sub Main mit XML-Ausgabe
dsDataSet.WriteXmlSchema("dtKlimataSchema.xml")
dsDataSet.WriteXml("dtKlimata.xml")
' ...
Nach dem Start der Anwendung erscheint eine Meldung, die durch
den Ereignishandler der Datentabelle ausgelöst wird.
Abbildung 5.31 Änderungsmeldung
Abbildung 5.32 Geänderte DataTable dtKlimata im DataSet
Die Datei dtKlimata.xml im Projektausgabeverzeichnis \bin enthält die
geänderten Daten der Datentabelle dtKlimata.
352
ADO.NET und Datenbanken
Eigenschaften von Änderungsereignissen
Änderungsereignisse der DataTable haben die folgenden Eigenschaften:
Eigenschaft
Beschreibung
Column
Gibt die Datenspalte zurück, deren Wert geändert wird
ProposedValue
Setzt oder liest den neuen Wert, der in die Spalte eingetragen werden soll
Row
Gibt die Datenzeile zurück, in der geändert wird
Tabelle 5.19 Eigenschaften des Änderungsereignisses einer DataTable
5.7.2
DataRow
Eine DataRow stellt eine einzelne Datenzeile in einer DataTable dar.
Werden Daten eingefügt oder geändert, die dem Tabellenschema
widersprechen, wird in der Eigenschaft RowError eine Fehlermeldung
gesetzt, die vor einer Aktualisierung der ursprünglichen Datenquelle
ausgewertet werden kann.
Eine DataRow wird über die NewRow-Methode einer Datentabelle
erzeugt.
dtDataTable.
NewRow
Listing 5.34 Erzeugung einer DataRow
Dim dtDataTable as DataTable
Dim drNeu as DataRow
drNeu = dtDataTable.NewRow()
drNeu("pkTabelle") = 1
drNeu("Testname") = "Neu"
drNeu("Beschreibung" ) = "Testzeile"
dtDatatable.Rows.Add(drNeu)
Eine neue Datenzeile wird immer mit dem Schema und den Spalteninformationen einer bestehenden DataTable angelegt. Sie kann auch
zuerst mit Daten gefüllt werden, um anschließend zur Auflistung der
Datenzeilen der DataTable hinzugefügt zu werden.
Datenobjekte des DataSets
353
DataRow-Eigenschaften
DataRow hat die folgenden Eigenschaften:
Name
Beschreibung
HasErrors
Gibt einen Booleschen Wert zurück, der angibt, ob in
der Datenzeile Fehler aufgetreten sind
Item
Setzt oder liest Daten in einer angegebenen Spalte
ItemArray
Gibt alle Werte dieser Zeile in einem Array zurück bzw.
setzt alle Werte dieser Zeile aus einem Array
RowError
Setzt oder liest eine Fehlerbeschreibung für eine Zeile
RowState
Gibt den aktuellen Zustand der Datenzeile zurück und
wird zur Auswertung von Datenänderungen genutzt
Table
Gibt die DataTable zurück, zu der diese DataRow gehört
Tabelle 5.20 DataRow-Eigenschaften
DataRow-Methoden
DataRow stellt die folgenden Methoden zur Verfügung:
Name
Beschreibung
AcceptChanges()
Übernimmt alle Änderungen, die an der DataRow seit
dem letzten Aufruf dieser Methode oder seit dem Laden
der DataRow vorgenommen wurden
BeginEdit()
Startet eine Änderungsaktion an der Datenzeile, während der alle Ereignisse deaktiviert sind. Diese Methode
wird zusammen mit EndEdit() und CancelEdit() verwendet.
CancelEdit()
Bricht die mit BeginEdit() eingeleitete Änderung der
Datenzeile ab
ClearErrors()
Mit dieser Methode werden alle Fehler gelöscht, die aus
Zeilenfehlern oder aus der Methode SetColumnError()
stammen
Delete()
Löscht die Datenzeile
EndEdit()
Beendet die Änderungen an der Datenzeile
Tabelle 5.21 DataRow-Methoden
354
ADO.NET und Datenbanken
Name
Beschreibung
GetChildRows()
Gibt die Detaildaten einer Datenzeile zurück. Diese
Methode wird mit einer DataRelation und der DataRowVersion aufgerufen.
GetColumnError()
Gibt die Fehlerbeschreibung einer Datenspalte zurück.
Wird mit einem DataColumn-Objekt, einer Ordinalzahl
oder dem Spaltennamen als Argument aufgerufen
GetColumnsInError()
Gibt ein Array der Spalten zurück, in denen Fehler aufgetreten sind
GetParentRow()
Gibt die der aktuellen Datenzeile übergeordnete Datenzeile zurück. Diese Methode wird mit einer DataRelation und der DataRowVersion aufgerufen.
GetParentRows()
Gibt ein Array der übergeordneten Datenzeilen zurück.
Diese Methode wird mit einer DataRelation und der
DataRowVersion aufgerufen.
HasVersion()
Gibt einen Booleschen Wert zurück, der angibt, ob eine
bestimmte Version der Datenzeile existiert. Wird mit
einem der DataRowVersion-Werte Current, Default,
Original oder Proposed als Argument aufgerufen.
IsNull()
Gibt einen Booleschen Wert zurück, der angibt, ob die
als Argument angegebene Spalte einen NULL-Wert enthält
RejectChanges()
Verwirft alle Änderungen, die an der Datenzeile gemacht
wurden, seit das letzte Mal AcceptChanges aufgerufen
wurde oder die Datenzeile geladen wurde
SetColumnError()
Setzt die Fehlerbeschreibung für die als Argument angegebene Datenspalte
SetParentRow()
Setzt die der aktuellen Datenzeile übergeordnete Datenzeile. Diese Methode wird mit einer Datenzeile oder
einer DataRelation und einer Datenzeile aufgerufen.
Tabelle 5.21 DataRow-Methoden (Forts.)
Die Verwendung der GetChildRows- und GetParentRows-Methoden wird in Abschnitt 5.8.1, »DataRelation«, beschrieben.
Werte der DataRowVersion
Die mit der HasVersion-Methode der DataRow ausgewertete Eigenschaft DataRowVersion kann folgende Werte annehmen:
Datenobjekte des DataSets
355
Eigenschaft
Beschreibung
Current
Aktueller Wert der Datenzeile
Default
Zustand der Datenzeile, wenn für neu erstellte Datenzeilen Vorgabewerte definiert sind und angewendet
wurden
Original
Als unverändert behandelte Version der Datenzeile,
die nach der Erstellung oder dem Aufruf von
AcceptChanges vorliegt
Proposed
Wert nach der Änderung von Werten, bevor
AcceptChanges aufgerufen wurde
Tabelle 5.22 Mögliche Werte der DataRowVersion
5.7.3
DataView
Eine DataView ist eine optional gefilterte Sicht auf die Daten in einem
DataSet. Es können mehrere DataViews für ein DataSet erstellt werden, um damit Daten zu filtern, zu sortieren oder zu suchen. Steuerelemente können direkt an DataViews gebunden werden. Für eine
DataView können Eigenschaften festgelegt werden, die von jenen der
Datentabelle abweichen, d.h., Sie können Daten filtern, sortieren und
vor dem Editieren oder Löschen schützen, ohne die zugrunde liegende DataTable zu verändern.
Beispiel
dvDropDown
In einem einfachen Beispiel soll ein Dropdown mit den Gewürznamen aus einer DataView gefüllt werden. Der Inhalt des Dropdowns
soll sortiert und gefiltert werden. Sie finden das fertige Projekt auf der
beiliegenden CD-ROM unter \Kapitel05\dvDropDown.
Legen Sie eine neue Windows-Anwendung an, blenden Sie in der
Entwurfsansicht von Form1 die Toolbox ein und platzieren Sie aus
dem Abschnitt Windows Forms eine Combobox auf der Maske, die Sie
im Eigenschaftenfenster DropDown benennen.
Auf der Maske sollen außer dem Gewürznamen im Dropdown auch
die anderen Daten des ausgewählten Gewürzes angezeigt werden.
Ziehen Sie dazu ein Label auf die Form1, das Sie lblSelectedID nennen, und fügen Sie drei Textboxen mit den Namen txtName, txtGattung und txtBeschreibung hinzu.
356
ADO.NET und Datenbanken
Fügen Sie in der Code-Ansicht von Form1 Verweise auf die Namensräume ConfigurationSettings, OleDB und SqlClient hinzu:
Imports System.Configuration.ConfigurationSettings
Imports System.Data.OleDb
Imports System.Data.SqlClient
Deklarieren Sie ein DataSet, eine DataTable und eine DataView.
Dim dsDropDown As DataSet
Dim dtDropDown As DataTable
Dim dvDropDown As DataView
In der Prozedur DropDown_Füllen soll ein DataSet über einen
Datenadapter mit einer einzigen Tabelle gefüllt werden. Der Beispielcode verwendet Datenprovider-unabhängigen Code, das Beispiel
funktioniert daher sowohl mit einer Access-Datenbank im Anwendungsverzeichnis \bin als auch mit der Datenbank dbWürzen auf dem
SQL Server. Die Information über den zu wählenden Datenprovider
wird zur Laufzeit der Konfigurationsdatei <Anwendungsname>.
exe.config entnommen.
Listing 5.35 Code zum Füllen des Dropdowns aus einer DataView
Private Sub DropDown_Füllen()
Dim _str As String
_str = "SELECT pkGewürz,Gewürzname, " & _
"
Gattung,Beschreibung " & _
" FROM tblGewürze"
' Allgemeiner Datenbankzugriff
Dim _conn As IDbConnection
Dim _cmd As IDbCommand
Dim _da As IDbDataAdapter
' Verbindungsauswahl auswerten
Select Case AppSettings("Verbindungstyp").ToUpper
Case "SQL"
_conn = New SqlConnection( _
AppSettings("conSQL"))
_cmd = New SqlCommand(_str)
_cmd.Connection = _conn
_da = New SqlDataAdapter(_cmd)
Case "OLEDB"
_conn = New OleDbConnection( _
AppSettings("conOleDB"))
Datenobjekte des DataSets
357
_cmd = New OleDbCommand(_str)
_cmd.Connection = _conn
_da = New OleDbDataAdapter(_cmd)
End Select
' DataSet über den DataAdapter füllen lassen
dsDropDown = New DataSet()
_da.Fill(dsDropDown)
' DataView für DataSet erstellen
dtDropDown = dsDropDown.Tables(0)
dvDropDown = dsDropDown.DefaultViewManager. _
CreateDataView(dtDropDown)
' DropDown an DataView binden
DropDown.DataSource = dvDropDown
DropDown.ValueMember = "pkGewürz"
DropDown.DisplayMember = "Gewürzname"
End Sub
Die DataView wird mit Hilfe des DefaultViewManagers des DataSets
erstellt, dessen CreateDataView-Methode auf die einzige Tabelle
angewendet wird.
Datenbindung
Das Dropdown wird über seine Eigenschaften DataSource, DataMember und ValueMember an die DataView gebunden. Die Textfeldinhalte
werden direkt an Tabellenspalten gebunden.
Listing 5.36 Datenbindung der Formularfelder
Private Sub Datenbindung_Formularfelder()
' Steuerelemente an DataView binden
Try
lblSelectedID.DataBindings.Add("Text", _
dvDropDown, "pkGewürz")
txtBeschreibung.DataBindings.Add("Text", _
dvDropDown, "Beschreibung")
txtGattung.DataBindings.Add("Text", _
dvDropDown, "Gattung")
txtGewürzname.DataBindings.Add("Text", _
dvDropDown, "Gewürzname")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
358
ADO.NET und Datenbanken
Der Code zum Füllen des Dropdowns sowie die Datenbindungen
werden beim Laden der Maske aus der Ereignisprozedur Form1_Load
aufgerufen.
Listing 5.37 Aktionen beim Laden der Maske
Private Sub Form1_Load( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles MyBase.Load
DropDown.Items.Clear()
DropDown_Füllen()
Datenbindung_Formularfelder()
End Sub
Wenn Sie das Projekt ausführen, sehen Sie als Ergebnis eine Datenmaske mit gefülltem Dropdown (siehe Abbildung 5.33).
Abbildung 5.33 Datenmaske mit Dropdown aus DataView
Filtern und Sortieren
Um die Sortierungs- und Filtermöglichkeiten der DataView zu testen,
fügen Sie der Maske eine Schaltfläche mit dem Namen btnFiltern
und eine Textbox mit dem Namen txtFilter hinzu.
Daten filtern und
sortieren mit
DataView
Klicken Sie doppelt auf btnFiltern, um den Code für die Filterbedingung anzugeben.
Datenobjekte des DataSets
359
Listing 5.38 Setzen der Filterbedingungen für die DataView
Private Sub btnFiltern_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnFiltern.Click
Try
' Filterbedingungen setzen
dvDropDown.Sort = "Gewürzname DESC"
dvDropDown.RowFilter = txtFilter.Text
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Bevor RowFilter verwendet werden kann, muss die Eigenschaft Sort
eine Sortierbedingung erhalten. Die Eigenschaft RowFilter entnimmt
die Filterbedingung der Textbox txtFilter.
Starten Sie die Anwendung und geben Sie in die neue Textbox als Filterbedingung "Gewürzname like 'K%' and id < 1023" ein. Öffnen
Sie das Dropdown, um die gefilterten Datensätze zu sehen.
Where und
Order by
Beide genannten Eigenschaften akzeptieren Fragmente von SQLAnweisungen, dabei funktioniert RowFilter ähnlich wie ein WHEREFilter in SQL, Sort ähnlich wie eine ORDER BY-Klausel.
Abbildung 5.34 Sortierte und gefilterte Daten im Dropdown
360
ADO.NET und Datenbanken
Daten suchen
Fügen Sie der Maske eine Schaltfläche mit dem Namen btnSuchen
und eine Textbox mit dem Namen txtSuche hinzu.
Um einzelne Datensätze gezielt zu suchen, wird zur Sicherheit zuerst
der Filter deaktiviert. Die Suche besteht im Auswerten der Methode
Find, die entweder die Zeilenposition der gesuchten Zeile oder, wenn
nichts gefunden wurde, -1 als Wert zurückgibt.
Methode Find()
Listing 5.39 Suchfunktion
Private Sub btnSuchen_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnSuchen.Click
' Filter für Suche zurücksetzen
txtFilter.Text = ""
dvDropDown.RowFilter = ""
dvDropDown.Sort = "Gewürzname ASC"
' Datensatznummer suchen
Dim _int As Integer
_int = dvDropDown.Find(txtSuche.Text)
' Zum gefundenen Datensatz navigieren
Me.BindingContext.Item(dvDropDown).Position = _int
End Sub
Abbildung 5.35 Suche in der Datenmaske
Datenobjekte des DataSets
361
Mit der Position der gefundenen Zeile kann über die Datenbindung
der Maske der Datensatz in allen gebundenen Steuerelementen angezeigt werden. Gibt es als Ergebnis einer Suche mehrere Datenzeilen,
speichert die Methode FindRows alle Fundstellen als Array.
DataView-Eigenschaften
Die Eigenschaften einer DataView sind:
Name
Eigenschaften
AllowDelete
Setzt oder liest den Wert, der festlegt, ob das Löschen
von Daten zugelassen ist
AllowEdit
Setzt oder liest den Wert, der festlegt, ob das Ändern
von Daten zugelassen ist
AllowNew
Setzt oder liest den Wert, der festlegt, ob Daten über
die Methode AddNew hinzugefügt werden dürfen
ApplyDefaultSort
Setzt oder liest den Wert, der festlegt, ob nach der
Standardvorgabe sortiert werden soll
Count
Liest die Anzahl der Datensätze aus, nachdem RowFilter und RowStateFilter angewendet worden sind
DataViewManager
Gibt den DataViewManager zurück, der mit dieser
DataView verbunden ist
Item
Identifiziert eine Datenzeile in der Datentabelle in der
Form dvDataView.Item(4)
RowFilter
Setzt oder liest die Zeichenkette, die eine Anweisung
enthält, mit der die Datenzeilen für die Sichtbarkeit in
der DataView gefiltert werden sollen
Sort
Setzt oder liest die Spalten, nach denen sortiert werden
soll, und legt die Sortierrichtung fest. Enthält die Spaltennamen und die Sortierrichtung ähnlich der SQLKlausel ORDER BY
Table
Setzt oder liest die DataTable, aus der die DataView
mit Daten versorgt wird. Diese Eigenschaft kann nur
von Nothing auf einen Wert gesetzt werden.
Tabelle 5.23 DataView-Eigenschaften
362
ADO.NET und Datenbanken
DataView-Methoden
Eine DataView stellt folgende Methoden zur Verfügung:
Name
Beschreibung
AddNew()
Fügt der DataView eine neue Datenzeile vom Typ DataRowView
hinzu
BeginInit() Mit BeginInit kann gesteuert werden, wann eine DataView
initialisiert wird, die auf einer Maske oder in einer Komponente
verwendet werden soll. Nach Aufruf von BeginInit wird zur
Laufzeit auf den Aufruf von EndInit gewartet, um sicherzustellen, dass die DataView nicht verwendet wird, bevor sie vollständig initialisiert wurde.
Delete()
Löscht eine Datenzeile an der angegebenen Zeilenposition in
der Form dvDataView.Delete(4)
EndInit()
Gibt das Ende der Initialisierung der DataView in Verbindung
mit BeginInit() zur Laufzeit an und gibt sie zur Verwendung
frei
Find()
Gibt die Zeilenposition einer zu suchenden Datenzeile zurück,
die über einen oder mehrere in Sort definierte Schlüsselwerte
identifiziert wird
FindRows()
Gibt die Zeilenposition mehrerer zu suchender Datenzeilen als
Array zurück, die über einen oder mehrere in Sort definierte
Schlüsselwerte identifiziert werden
Tabelle 5.24 DataView-Methoden
5.8
Tabellenverknüpfungen
Das Laden mehrerer Tabellen in ein DataSet überträgt keine in der
Datenbank definierten Tabellenverknüpfungen. Unter Umständen
fehlen deshalb im DataSet notwendige Beziehungen, mit denen in
der Datenbank die Datenintegrität überwacht wird. Sie können diese
Beziehungen im DataSet über DataRelation-Objekte entsprechend
nachbilden oder auch bewusst abweichend definieren.
5.8.1
Verknüpfte
Tabellen
DataRelation
DataRelation-Objekte sind Teil des DataSets und werden zur Herstellung von Tabellenbeziehungen durch die Verknüpfung von überund untergeordneten Datenspalten desselben Datentyps verwendet.
Diese Beziehungen legen Regeln und Abhängigkeiten für die Datenin-
Tabellenverknüpfungen
363
tegrität in den beteiligten Datentabellen fest und werden bei allen
Aktionen im DataSet überwacht.
Mit Hilfe von DataRelation-Objekten ist es auch möglich, hierarchische Datenmengen programmatisch zu erzeugen.
DataRelation anlegen
Eine DataRelation wird zwischen einer oder mehreren Spalten in
einer Hauptdatentabelle und einer gleichen Anzahl von Spalten in
einer Detaildatentabelle erzeugt.
Beispiel
rlDataGrid
Im folgenden Beispiel wird eine Beziehung zwischen zwei Datentabellen im Programmcode erzeugt. Der Vorgang entspricht inhaltlich
dem in Abbildung 5.24 dargestellten Einrichten einer Tabellenbeziehung aus der Schemadarstellung eines DataSets. Sie finden das fertige Beispiel rlDataGrid auf der beiliegenden CD-ROM im Verzeichnis
\Kapitel05\rlDataGrid.
Legen Sie eine neue Windows-Anwendung an und ziehen Sie in der
Entwurfsansicht ein DataGrid aus der Toolbox auf die Maske Form1.
Fügen Sie im Projektmappen-Explorer die Datei App.config hinzu, in
der Sie wie gehabt die Verbindungszeichenketten verschiedener
Datenbanken speichern können. Der im Folgenden gezeigte Beispielcode funktioniert gleichermaßen mit der SQL Server- und der AccessVariante der Datenbank dbWürzen.
Fügen Sie in der Code-Ansicht von Form1 Verweise auf die Namensräume ConfigurationSettings, OleDb und SqlClient hinzu:
Imports System.Configuration.ConfigurationSettings
Imports System.Data.OleDb
Imports System.Data.SqlClient
Deklarieren Sie ein DataSet ds, das die zu verknüpfenden Tabellen
aufnehmen soll:
Private ds As New DataSet()
Das Laden der Daten findet in der Prozedur TabellenLaden statt, die
aus der Ereignisprozedur Form1_Load aufgerufen wird.
364
ADO.NET und Datenbanken
Listing 5.40 Laden der Tabellen
Private Sub TabellenLaden()
' SQL Anweisungen
Dim _str1 As String = "SELECT *
" & _
" FROM tblKlimata "
Dim _str2 As String = "SELECT *
" & _
" FROM tblRegionen"
' Allgemeiner Datenbankzugriff
Dim _conn As IDbConnection
Dim _cmd1 As IDbCommand
Dim _cmd2 As IDbCommand
' Verbindungsauswahl auswerten
Select Case AppSettings("Verbindungstyp").ToUpper
Case "SQL"
_conn = New SqlConnection( _
AppSettings("conSQL"))
_cmd1 = New SqlCommand(_str1)
_cmd1.Connection = _conn
_cmd2 = New SqlCommand(_str2)
_cmd2.Connection = _conn
' DataSet über DataAdapter füllen
Dim _da1 As SqlDataAdapter
_da1 = New SqlDataAdapter(_cmd1)
_da1.Fill(ds, "Klimata")
Dim _da2 As SqlDataAdapter
_da2 = New SqlDataAdapter(_cmd2)
_da2.Fill(ds, "Regionen")
Case "OLEDB"
_conn = New OleDbConnection( _
AppSettings("conOleDB"))
_cmd1 = New OleDbCommand(_str1)
_cmd1.Connection = _conn
_cmd2 = New OleDbCommand(_str2)
_cmd2.Connection = _conn
' DataSet über DataAdapter füllen
Dim _da1 As OleDbDataAdapter
_da1 = New OleDbDataAdapter(_cmd1)
_da1.Fill(ds, "Klimata")
Dim _da2 As OleDbDataAdapter
_da2 = New OleDbDataAdapter(_cmd2)
Tabellenverknüpfungen
365
_da2.Fill(ds, "Regionen")
End Select
End Sub
Die Daten werden in die Tabellen Klimata und Regionen des DataSets
geladen, für die in der Prozedur TabellenVerknüpfen eine Beziehung
definiert wird.
Listing 5.41 Erzeugen einer DataRelation
Private Sub TabellenVerknüpfen()
' Tabellenbeziehung hinzufügen
ds.Relations.Add( _
New DataRelation( _
"relKlimataRegionen", _
ds.Tables("Klimata").Columns("pkKlima"), _
ds.Tables("Regionen").Columns("fkKlima")))
End Sub
Ähnlich wie bei einer JOIN-Anweisung in SQL wird die Zuordnung
über die Tabellennamen und die zu verknüpfenden Spalten angegeben. Im Gegensatz zur JOIN-Anweisung ist eine DataRelation auch
außerhalb von SQL-Anweisungen wirksam. Die Syntax lautet:
New DataRelation(RelationName, ParentColumn, ChildColumn)
Sollen mehrere Spalten gleichzeitig verknüpft werden, kann anstelle
einer einzelnen DataColumn auch ein Array von Datenspalten angegeben werden.
Die neu erstellte Tabellenbeziehung wird erst mit der Einfügung in die
Auflistung der DataRelations des DataSets wirksam.
DataRelation-Eigenschaften
Ein DataRelation-Objekt hat folgende Eigenschaften:
Name
Beschreibung
ChildColumns
Ruft die untergeordneten DataColumn-Objekte
dieser Beziehung ab
ChildKeyConstraint
Ruft die ForeignKeyConstraint für die Beziehung
ab
Tabelle 5.25 DataRelation-Eigenschaften
366
ADO.NET und Datenbanken
Name
Beschreibung
ChildTable
Ruft die untergeordnete Tabelle dieser Beziehung ab
DataSet
Ruft das DataSet ab, zudem die DataRelation
gehört
ExtendedProperties
Ruft die Auflistung ab, in der angepasste Eigenschaften gespeichert werden
Nested
Ruft einen Wert ab, der angibt, ob DataRelationObjekte geschachtelt sind, oder legt diesen fest
ParentColumns
Ruft ein Array von DataColumn-Objekten ab, die die
übergeordneten Spalten dieser DataRelation sind
ParentKeyConstraint Ruft die UniqueConstraint ab, durch die sichergestellt wird, dass Werte in der übergeordneten Spalte
einer DataRelation eindeutig sind
ParentTable
Ruft die übergeordnete DataTable dieser DataRela-
tion ab
RelationName
Ruft den Namen ab, der zum Abrufen einer DataRelation aus der DataRelationCollection verwendet wird, oder legt diesen fest
Tabelle 5.25 DataRelation-Eigenschaften (Forts.)
5.8.2 Darstellung verknüpfter Tabellen
Zur Anzeige der verknüpften Tabellen in einem DataGrid genügt es,
dessen DataSource-Eigenschaft an die Tabelle mit den Hauptdaten zu
binden.
Listing 5.42 Anzeigen der verknüpften Daten
Private Sub TabellenAnzeigen()
' Verknüpfte Tabellen in einem DataGrid anzeigen
DataGrid1.DataSource = ds.Tables("Klimata")
End Sub
Ergänzen Sie in Form1_Load den Aufruf der Prozeduren TabellenVerknüpfen und TabellenAnzeige und starten Sie dann die Anwendung
rlDataGrid.
Listing 5.43 Aufruf der Verknüpfung und Anzeige
Private Sub Form1_Load( _
ByVal sender As Object, _
Tabellenverknüpfungen
367
ByVal e As System.EventArgs) _
Handles MyBase.Load
Call TabellenLaden()
Call TabellenVerknüpfen()
Call TabellenAnzeigen()
End Sub
Abbildung 5.36 Anzeige verknüpfter Tabellen im DataGrid
Im DataGrid wird zusätzlich zum Inhalt der Hauptdatentabelle Klimata unter jedem Knoten im Zeilenkopf ein Link eingeblendet, der
den Namen der DataRelation relKlimataRegionen trägt. Wenn Sie
diesem Link folgen, wird im DataGrid danach die Detaildatentabelle
Regionen angezeigt (siehe Abbildung 5.37). Dieser Vorgang geschieht
selbsttätig durch Interaktion des DataGrids mit den Methoden der
DataRelation, d.h. ohne einen Wechsel der Datenbindung.
Abbildung 5.37 Anzeige einer verknüpften Detaildatentabelle im DataGrid
Navigation im
DataGrid
368
In der Kopfzeile des DataGrids werden in der Detaildatenanzeige der
Tabellenname und der Inhalt des Hauptdatensatzes angezeigt. Über
die Symbole auf der rechten Seite kann zur Hauptdatentabelle zurück
navigiert oder die Hauptdatensatzinformation ausgeblendet werden.
Die Detaildatentabelle könnte im Prinzip Links auf weitere Untertabellen enthalten, die auf die gleiche Art zu erreichen wären.
ADO.NET und Datenbanken
In diesem Beispiel wird nur eine einfache Relation zwischen zwei
Tabellen dargestellt. Ein komplexeres Anwendungsbeispiel von Relationen mehrerer hierarchisch verknüpfter Tabellen finden Sie im
Abschnitt 5.11.1, »Datengebundenes Treeview«.
5.8.3 Zugriff auf Detaildaten
Der Zugriff auf die Detaildaten in einer verknüpften Tabelle lässt sich
auch programmatisch einrichten. Die Detaildaten einer DataRelation
sind über die GetChildRows-Methode einer einzelnen DataRow in der
Hauptdatentabelle zugänglich und werden dabei in ein Array von
Datenzeilen übertragen. Der Aufruf folgt der Syntax:
RowArray() = ds.Tables(TabellenName).Rows(Index). _
GetChildRows(RelationName)
GetChildRows testen
Erweitern Sie die Anwendung um eine Prozedur zum Test der GetChildRows-Methode, die beim Klick auf einen Datensatz in der
Hauptdatentabelle ausgelöst wird. Wählen Sie dazu aus dem linken
Codenavigations-Dropdown DataGrid1, dann aus dem rechten
MouseDown, um die beim Klick auf das DataGrid1 auszulösende Ereignisprozedur anzulegen.
Der Klick auf das DataGrid lässt sich über dessen HitTest-Methode
auswerten, die die Koordinaten einer angeklickten Tabellenzelle
zurückgibt. Für die DataRow in der dazugehörigen Tabellenzeile wird
im Beispiel mit GetChildRows die Anzahl der vorhandenen Detaildatensätze ausgelesen und im Ausgabefenster angezeigt. Starten Sie die
Anwendung im Debugmodus, um die Ausgabe der Testprozedur zu
sehen.
HitTest
Listing 5.44 Zugriff auf die Detaildaten einer verknüpften Tabelle
Private Sub DataGrid1_MouseDown( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles DataGrid1.MouseDown
' Koordinaten des Klicks auf das DataGrid auswerten
Dim htHitTest As _
System.Windows.Forms.DataGrid.HitTestInfo
Tabellenverknüpfungen
369
htHitTest = DataGrid1.HitTest(e.X, e.Y)
If htHitTest.Row >= 0 Then
' Datenzeile im DataGrid ermitteln
Dim _intRow As Integer
_intRow = DataGrid1.Item(htHitTest.Row - 1, 0)
' Detaildaten der Relation in Array laden
Dim arrRow() As DataRow
arrRow = ds.Tables("Klimata").Rows(_intRow). _
GetChildRows("relKlimataRegionen")
Console.WriteLine()
Console.Write(" Es sind diesem Klima " & _
arrRow.Length & " Regionen zugeordnet")
End If
End Sub
GetParentRows
In gleicher Weise erhalten Sie Zugriff auf den Hauptdatensatz eines
Detaildatensatzes, indem Sie die GetParentRows-Methode einer
DataRow aus der Detaildatentabelle anwenden. In diesem Fall ist das
Ergebnis kein Array, sondern ein einzelner Datensatz.
5.9
Datenauswahl und Datenaktualisierung
Wie im Abschnitt 5.5, »Datenadapter für das DataSet«, dargestellt
wurde, verwenden Datenadapter zum Aktualisieren, Löschen und
Einfügen von Datensätzen in der Datenbank verschiedene CommandObjekte mit eigenen SQL-Anweisungen. Bei der Verwendung eines
Assistenten zur Konfiguration eines Datenadapters, wie es in
Abschnitt 5.5.2, »Datenadapter mit Assistenten konfigurieren«,
beschrieben ist, werden für eine gegebene SQL-Anweisung zur
Datenauswahl automatisch die entsprechenden SQL-Anweisungen
oder Gespeicherten Prozeduren zur Aktualisierung der Datenbank
erstellt.
Automatische
Aktualisierung
370
Das Objekt, das diese Aufgabe übernimmt, ist der CommandBuilder,
der auch außerhalb des Datenadapter-Assistenten eingesetzt werden
kann. Gegenüber manuell angelegten Command-Objekten zur Aktualisierung der Datenbank hat die Verwendung eines CommandBuilderObjektes den Vorteil, dass nach einer Änderung der SQL-Anweisung
ADO.NET und Datenbanken
für die Datenauswahl die SQL-Anweisungen zur Datenaktualisierung
automatisch erneuert werden.
5.9.1 CommandBuilder
Die Verwendung des CommandBuilders im Programmcode ist denkbar einfach: Für ein manuell erstelltes DataAdapter-Objekt, das mit
einem gültigen Command-Objekt zum Füllen eines DataSets verwendet wird, genügt die bloße Erzeugung eines CommandBuilder-Objektes, um die Aktualisierung der Datenquelle zu ermöglichen.
Listing 5.45 CommandBuilder für Datenadapter erzeugen
' Pseudocode zur Verdeutlichung des Verfahrens
_cmd = New SqlCommand(SELECT-Anweisung)
' DataAdapter initialisieren
_da = New SqlDataAdapter(_cmd)
_da.Fill(ds)
' Datenmanipulation in der Anwendung
' ...
Try
' INSERT-, DELETE- und UPDATE-Anweisungen
' vom CommandBuilder erstellen lassen
Dim _cb As New SqlCommandBuilder(_da)
_da.Update(ds)
Catch ex As Exception
' ...
End Try
Ein CommandBuilder-Objekt wird für einen einzelnen DataAdapter
angelegt und stellt die automatisch erzeugten SQL-Anweisungen über
seine Methoden GetInsertCommand, GetDeleteCommand und GetUpdateCommand zur Verfügung. Diese Methoden können zum Auslesen
der SQL-Anweisungen genutzt werden, um diese manuell zu verändern oder zu ergänzen.
Listing 5.46 CommandBuilder-Anweisungen auslesen
' Pseudocode zur Verdeutlichung des Verfahrens
_cmd = New SqlCommand(SELECT-Anweisung)
' DataAdapter initialisieren
_da = New SqlDataAdapter(_cmd)
_da.Fill(ds)
Datenauswahl und Datenaktualisierung
371
' Datenmanipulation in der Anwendung
' ...
Try
Dim _cb As New SqlCommandBuilder(_da)
' INSERT-, DELETE- und UPDATE-Anweisungen
' des CommandBuilders auslesen
' und ggf. nachbearbeiten
_da.InsertCommand = _cb.GetInsertCommand
_da.DeleteCommand = _cb.GetDeleteCommand
_da.UpdateCommand = _cb.GetUpdateCommand
_da.Update(ds)
Catch ex As Exception
' ...
End Try
Der CommandBuilder versagt bei der Verwendung von unzulässigen
Tabellen- und Spaltennamen, die vordefinierten Begriffen entsprechen (siehe Abschnitt 4.2.3, »Benennungsregeln«). Es ist möglich, mit
Hilfe der Eigenschaften QuotePrefix und QuoteSuffix diese Namen
ähnlich wie in Access in die Klammerzeichen [ ] einzubetten, um
damit einige Probleme des CommandBuilders zu vermeiden. Die bessere Methode ist aber immer noch die konsequente Vermeidung vordefinierter Begriffe.
CommandBuilder-Eigenschaften
Ein CommandBuilder-Objekt hat folgende Eigenschaften:
Name
Beschreibung
DataAdapter
Ruft ein DataAdapter-Objekt ab, für das automatisch SQLAnweisungen oder Transact-SQL-Anweisungen generiert werden, oder legt dieses fest
QuotePrefix
Ruft das oder die Anfangszeichen ab, die beim Angeben von
Namen für SQL Server-Objekte (z.B. Tabellen oder Spalten)
verwendet werden sollen, die Zeichen wie Leerzeichen
enthalten, oder legt diese fest
QuoteSuffix
Ruft das oder die Endzeichen ab, die beim Angeben von
Namen für SQL Server-Objekte (z.B. Tabellen oder Spalten)
verwendet werden sollen, die Zeichen wie Leerzeichen
enthalten, oder legt diese fest
Tabelle 5.26 Eigenschaften des CommandBuilder-Objektes
372
ADO.NET und Datenbanken
CommandBuilder-Methoden
Ein CommandBuilder-Objekt stellt folgende Methoden zur Verfügung:
Name
Beschreibung
DeriveParameters
Füllt die Parameter-Auflistung des angegebenen
Command-Objekts mit den Parameterinformationen für
eine in Command angegebene Gespeicherte Prozedur auf
GetDeleteCommand
Ruft das für Löschvorgänge in der Datenbank erforderliche, automatisch generierte Command-Objekt ab, wenn
eine Anwendung Update für DataAdapter aufruft
GetInsertCommand
Ruft das für Einfügevorgänge in der Datenbank erforderliche, automatisch generierte Command-Objekt ab,
wenn eine Anwendung Update für DataAdapter aufruft
GetUpdateCommand
Ruft das für Aktualisierungen in der Datenbank erforderliche, automatisch generierte Command-Objekt ab,
wenn eine Anwendung Update für DataAdapter aufruft
RefreshSchema
Aktualisiert die Schemainformationen der Datenbank,
die zum Generieren von INSERT-Anweisungen, UPDATEAnweisungen und DELETE-Anweisungen verwendet
werden
Tabelle 5.27 Methoden des CommandBuilder-Objektes
5.9.2 Aktualisieren von Autowerten
Bei Verwendung des CommandBuilders in einer Anwendung, die es
erlaubt, im DataSet neue Datensätze anzulegen, ist es notwendig, das
Aktualisieren von Datentabellen mit automatischer Schlüsselwertvergabe genauer zu planen:
왘
Im DataSet und in der Datenbank müssen unterschiedliche Autowerte für neue Datensätze vergeben werden, die sich nicht überschneiden sollten. Am einfachsten ist dies über die Definition von
negativen Autowerten für Schlüsselfelder im DataSet zu erreichen
(siehe auch das Beispiel in Abschnitt 7.2.4, »Webdienst zur Aktualisierung der Datenbank«).
왘
Beim Einfügen der neuen Datensätze sollten in der Datenbank neu
vergebene Autowerte auch in das DataSet der Anwendung über-
Datenauswahl und Datenaktualisierung
373
tragen werden, um dort mit den aktualisierten Schlüsselwerten
weiter zu arbeiten.
Der zuletzt von der Datenbank erzeugte Schlüsselwert kann mit
Hilfe der Systemvariablen des SQL Servers @@IDENTITY, die auch in
Access 2000 zur Verfügung steht, ausgelesen werden. Der SQL Server 2000 bietet alternativ den funktionsgleichen Aufruf scope_
identity, der sicherstellt, dass der erzeugte Schlüsselwert tatsächlich aus der Einfügeaktion innerhalb der eigenen Transaktion entstanden ist.
@@identity und
scope_identity()
Für die erfolgreiche Aktualisierung des DataSets mit automatisch
erzeugten Schlüsselwerten sind zwei Voraussetzungen erforderlich:
왘
Die INSERT-Anweisung muss mit einer zweiten Anweisung, die
@@IDENTITY oder scope_identity abfragt, ergänzt werden.
왘
Die UpdatedRowSource-Eigenschaft des InsertCommand-Objektes
muss auf FirstReturnedRecord gesetzt werden, damit die entsprechende Datenzeile des DataSets mit dem neuen Datenschlüssel
aktualisiert wird.
Listing 5.47 Vorbereitung zur Aktualisierung von Autowerten
' Pseudocode zur Verdeutlichung des Verfahrens
_cmd = New SqlCommand(SELECT-Anweisung)
' DataAdapter initialisieren
_da = New SqlDataAdapter(_cmd)
_da.Fill(ds)
' Datenmanipulation in der Anwendung
' ...
Try
Dim _cb As New SqlCommandBuilder(_da)
' INSERT-, DELETE- und UPDATE-Anweisungen
' des CommandBuilders auslesen
' und ggf. nachbearbeiten
_da.InsertCommand = _cb.GetInsertCommand
_da.InsertCommand.CommandText =
_da.InsertCommand.CommandText & ";
" & _
"SELECT * FROM Tabelle
" & _
"WHERE pkTabelle = @@IDENTITY"
_da.InsertCommand.UpdatedRowSource = _
UpdateRowSource.FirstReturnedRecord
374
ADO.NET und Datenbanken
_da.DeleteCommand = _cb.GetDeleteCommand
_da.UpdateCommand = _cb.GetUpdateCommand
_da.Update(ds)
Catch ex As Exception
' ...
End Try
Die Aktualisierung von Autowerten im DataSet wird für SqlDataAdapter, die Sie mit dem Visual Studio .NET-Assistenten erstellen, in
der beschriebenen Weise automatisch eingerichtet. Dies gilt nicht für
OleDbDataAdapter, da nicht alle OleDB-Datenquellen eine Variable
@@IDENTITY unterstützen. Wenn Sie Access 2000-Datenbanken verwenden oder über den OleDb-Datenprovider auf den SQL Server
zugreifen, sollten Sie den Aktualisierungscode entsprechend manuell
anpassen.
5.10 Datenbindung und Datennavigation
Die Datenbindung in Masken dient dazu, Steuerelemente so mit
Datenquellen zu verbinden, dass ausgewählte Inhalte der Datenquelle in diesen Steuerelementen angezeigt werden und Änderungen,
die an Inhalten in den Steuerelementen vorgenommen werden, gegebenenfalls die Datenquelle aktualisieren.
5.10.1 BindingContext
Datengebundene Steuerelemente sind keine Neuerung von Visual
Basic .NET, sondern waren schon mit ADO(alt) in Visual Basic 6 verfügbar. Anders als in Visual Basic 6 ist kein separates ADODataControl
zur Datenbindung und zur Navigation in den Datensätzen einer
Datenquelle erforderlich, da Visual Studio .NET-Masken ein BindingContext-Objekt besitzen, das diese Aufgabe übernimmt.
BindingContext
Im .NET Framework ist eine Datenbindung an alle Objekte möglich,
die wie Arrays, Collections oder ADO.NET-Datenobjekte eine IListSchnittstelle implementieren. Es spielt keine Rolle, ob Sie ein
ADO.NET-Datenobjekt oder eine Objekt-Klasse als Datenquelle wählen. Im Prinzip kann jede Eigenschaft eines Steuerelement-Objektes
an eine Datenquelle gebunden werden, was unter anderem eine
direkte Steuerung des Erscheinungsbildes von Anwendungen durch
Datenbankinhalte möglich macht.
Datenbindung an
Datenobjekte,
Klassen und
Arrays
Datenbindung und Datennavigation
375
Es ist wichtig zu verstehen, dass, anders als in einer Recordset-Verarbeitung mit Visual Basic 6, in ADO.NET kein »aktueller« Datensatz
einer Bearbeitungsschleife als Bezug für die Datenbindung mehrerer
Steuerelemente zur Verfügung steht. Die Bindung von ausgewählten
Steuerelementen an Inhalte aus der gleichen Datenquelle wird über
ein BindingManagerBase-Objekt bestimmt, das aus dem BindingContext einer Maske abgeleitet ist. Die Navigation in den Datensätzen erfolgt über die Position-Eigenschaft des BindingContexts.
Einfache und komplexe Datenbindungen
Datenbindungen für einzelne Steuerelementinhalte einer Maske werden der Auflistung der Datenbindungen (DataBindings) durch deren
Add-Methode hinzugefügt. Bei komplexen Datenbindungen wie z.B.
dem Füllen eines Dropdowns aus einer Detaildatentabelle werden
dessen Bindungen über ein eigenes CurrencyManager-Objekt verwaltet und angesprochen, auf das der BindingContext verweist.
Listing 5.48 Beispiel für eine einfache Datenbindung
txtGewürzname.DataBindings.Add("Text", _
dsDataSet.Tables("Gewürze"), "Gewürzname")
txtGattung.DataBindings.Add("Text", _
dsDataSet.Tables("Gewürze"), "Gattung")
Listing 5.49 Beispiel für eine komplexe Datenbindung
DropDown.DataSource = dvDataView
DropDown.ValueMember = "fkGewürz"
DropDown.DisplayMember = "Bildname"
DropDown.DataBindings.Add("SelectedValue", _
dsDataSet.Tables("Gewürze"), "pkGewürz")
Beide Bindungstypen lassen sich Steuerelementen zur Entwurfszeit
sehr leicht auch im Eigenschaftenfenster der Visual Studio .NET-IDE
zuordnen (siehe Abbildung 5.38).
376
ADO.NET und Datenbanken
Abbildung 5.38 Zuweisen der Datenbindung in der Visual Studio .NET-IDE
Eigenschaften der BindingManagerBase
Ein BindingManagerBase-Objekt hat folgende Eigenschaften:
Name
Beschreibung
Bindings
Ruft die Auflistung verwalteter Bindungen ab
Count
Ruft beim Überschreiben in einer abgeleiteten
Klasse die Anzahl der Zeilen ab, die von BindingManagerBase verwaltet werden
Current
Ruft beim Überschreiben in einer abgeleiteten
Klasse das aktuelle Objekt ab
Position
Ruft beim Überschreiben in einer abgeleiteten
Klasse die Position in der zugrunde liegenden
Liste ab, auf die an diese Datenquelle gebundene
Steuerelemente zeigen, oder legt diese fest
Tabelle 5.28 Eigenschaften des BindingManagerBase-Objekts
Methoden der BindingManagerBase
Das BindingManagerBase-Objekt stellt folgende Methoden zur Verfügung:
Name
Beschreibung
AddNew()
Fügt beim Überschreiben in einer abgeleiteten
Klasse der zugrunde liegenden Liste ein neues
Element hinzu
Tabelle 5.29 Methoden des BindingManagerBase-Objekts
Datenbindung und Datennavigation
377
Name
Beschreibung
CancelCurrentEdit()
Bricht beim Überschreiben in einer abgeleiteten
Klasse den aktuellen Bearbeitungsvorgang ab
EndCurrentEdit()
Beendet beim Überschreiben in einer abgeleiteten
Klasse den aktuellen Bearbeitungsvorgang
GetItemProperties()
Ruft die Liste der Eigenschaftenbezeichner für die
Datenquelle ab oder legt diese fest
RemoveAt()
Löscht beim Überschreiben in einer abgeleiteten
Klasse die Zeile am angegebenen Index aus der
zugrunde liegenden Liste
ResumeBinding()
Setzt beim Überschreiben in einer abgeleiteten
Klasse die Datenbindung fort
SuspendBinding()
Unterbricht die Datenbindung beim Überschreiben in einer abgeleiteten Klasse
Tabelle 5.29 Methoden des BindingManagerBase-Objekts (Forts.)
Ereignisse der BindingManagerBase
Das BindingManagerBase-Objekt löst folgende Ereignisse aus:
Name
Beschreibung
CurrentChanged
Tritt ein, wenn sich der gebundene Wert ändert
PositionChanged
Tritt ein, wenn sich Position geändert hat
Tabelle 5.30 Ereignisse des BindingManagerBase-Objekts
5.10.2 Datenmaske mit Bildern
Das Beispiel AccessFormular diente bereits in Abschnitt 5.3, »Datengebundene Eingabemaske«, zur Darstellung der Datenbindung und der
Navigation in Datensätzen. Es soll an dieser Stelle erweitert werden
und die Anzeige der Bilder ermöglichen, die den Gewürzen in der
Datenbank dbWürzen zugeordnet sind. Dabei wird die FormatMethode des Binding-Objektes für das Bild-Steuerelement verwendet, um die Bilddaten aus der Datenbank zu laden. Ergänzend ist es in
diesem Beispiel möglich, vorhandene Bilder zu ersetzen und damit
neue Bilder in die Datenbank zu übertragen.
378
ADO.NET und Datenbanken
Bilder aus der Datenbank
Die Speicherung von Bildern in einer Datenbank galt mit ADO(alt)
als abwegige, da brüchige Lösung, die in jedem Fall zu vermeiden
war. In der Regel wurden zu Datensätzen gehörende Bilder als
Dateien in eigenen Verzeichnissen gespeichert und in der Datenbank lediglich die Pfadnamen verwaltet.
Im asynchronen Modell von ADO.NET und unter Berücksichtigung
der Fähigkeiten des .NET Frameworks kann auch diese Regel in
Frage gestellt werden.
왘
Nachdem .NET Framework-Anwendungen dynamisch aktualisierbar sind und auf mehreren Servern gleichzeitig mit den gleichen Daten laufen, stellt die Replikation von dateibasierten Bilddaten ein konzeptionelles Hindernis für flexible verteilte Anwendungen dar.
왘
Durch Verwendung der Klassen des .NET Frameworks verringert
sich der Aufwand erheblich, Bilder in eine Datenbank zu schreiben und sie in einem Datenstrom wieder auszulesen. Siehe dazu
auch das folgende Beispiel.
왘
Das Auslesen von Bildern in den Dateigrößen, wie sie z.B. in
Webanwendungen Verwendung finden, verursacht mit aktuellen Datenbankservern keine Performance-Probleme.
왘
Mit ADO.NET können Sie auf verschiedene Datenbanktypen
gleichzeitig zugreifen und deren Daten gemeinsam verarbeiten –
es gibt daher mehrere, auch kombinierte Lösungen mit XMLDatenmengen, die eine Speicherung von Bildern in Einzeldateien überflüssig machen.
Das Speichern von kleinen Bildern in der Datenbank ist, was die
Handhabung, den Aufwand zur Aktualisierung, die Möglichkeiten
zum Backup und die direkte Verfügbarkeit betrifft, mit ADO.NET
deutlich gegenüber einer dateibasierten Lösung zu bevorzugen.
Sie finden das fertige Beispiel AccessFormularBild auf der beiliegenden
CD-ROM im Verzeichnis \Kapitel05\AccessFormularBild. Sie können
auch, wie im Folgenden beschrieben, das Beispielprojekt AccessFormular fortsetzen.
Datenbindung und Datennavigation
Beispiel AccessFormularBild
379
Datenbindung einrichten
Einige Datenbindungen für die Bilder- und Gewürztabellen sind
bereits aus dem vom Assistenten erstellten Beispielprojekt AccessFormular vorhanden (siehe Listing 5.50).
Listing 5.50 Vorhandene Datenbindung im Beispielprojekt AccessFormular
Me.BindingContext(objdsWürzen, "tblGewürze")
Me.BindingContext(objdsWürzen, "tblBilder")
Fügen Sie in der Entwurfsansicht von DatenFormular aus dem
Abschnitt Windows Forms der Toolbox ein PictureBox-Element
PictureBox1 hinzu.
Die Bindung für das PictureBox-Steuerelement soll beim Laden des
Datenformulars gesetzt werden. Wählen Sie daher aus dem linken
Codenavigations-Dropdown Basisklassenereignisse, dann aus
dem rechten Load, um die Ereignisprozedur für das Laden von DatenFormular anzulegen.
Format-Ereignis
nutzen
Tragen Sie in DatenFormular_Load eine einfache Datenbindung für
die PictureBox1 an das Datenbankfeld tblBilder.Foto ein und
definieren Sie zwei BindingManagerBase-Objekte für die vorhandenen Datenbindungen.
Listing 5.51 DatenFormular_Load
Private Sub DatenFormular_Load( _
ByVal sender As Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Datenbindung für die PictureBox einrichten
PictureBox1.DataBindings.Add("Image", _
objdsWürzen, "tblBilder.Foto")
' Format-Ereignis zum Laden des Bildes einsetzen
AddHandler PictureBox1.DataBindings("Image"). _
Format, AddressOf FotoFormatieren
' BindingManagerBase für Hauptdaten einrichten
bmbGewürze = Me.BindingContext( _
objdsWürzen, "tblGewürze")
' BindingManagerBase für Detaildaten einrichten
bmbBilder = Me.BindingContext( _
objdsWürzen, "tblBilder")
End Sub
380
ADO.NET und Datenbanken
Zur Übertragung der Bilddaten in die PictureBox1 wird im Beispiel
das Format-Ereignis des Binding-Objektes genutzt. Dieses Ereignis
tritt immer dann auf, wenn Daten aus der Datenquelle an ein Steuerelement gesendet und dabei umgewandelt bzw. formatiert werden.
Mit Hilfe der Anweisung:
AddHandler PictureBox1.DataBindings("Image"). _
Format, AddressOf FotoFormatieren
wird der Formatierungsvorgang an eine eigene Prozedur FotoFormatieren delegiert.
Binding-Ereignisse
Ein Binding-Objekt kann folgende Ereignisse auslösen:
Name
Beschreibung
Format
Tritt ein, wenn die Eigenschaft eines Steuerelements an einen Datenwert gebunden ist
Parse
Tritt ein, wenn der Wert eines datengebundenen
Steuerelements geändert wird
Tabelle 5.31 Ereignisse des Binding-Objektes
Binding-Eigenschaften
Ein Binding-Objekt hat folgende Eigenschaften:
Name
Beschreibung
BindingManagerBase
Ruft die BindingManagerBase dieser Bindung ab
BindingMemberInfo
Ruft ein Objekt mit Informationen über die Bindung ab, die auf dem dataMember-Parameter im
Binding-Konstruktor basieren
Control
Ruft das Steuerelement ab, zu dem die Bindung
gehört
DataSource
Ruft die Datenquelle für diese Bindung ab
Tabelle 5.32 Eigenschaften des Binding-Objektes
Datenbindung und Datennavigation
381
Name
Beschreibung
IsBinding
Ruft einen Wert ab, der angibt, ob die Bindung
aktiv ist
PropertyName
Ruft den Namen der Eigenschaft zur Datenbindung
des Steuerelements ab oder legt diesen fest
Tabelle 5.32 Eigenschaften des Binding-Objektes (Forts.)
FotoFormatieren
Die Prozedur FotoFormatieren wird vom Ereignis Format des Binding-Objektes aufgerufen, wenn der aktuelle Datensatz über die
Position des BindingContexts geändert wird und Bilddaten in die
Picturebox1 geladen werden.
Der Aufbau einer Prozedur zur Formatierung von Daten folgt einem
einfachen Prinzip: Die Daten sind im Ereignis-Argument e enthalten,
werden in der Prozedur umgewandelt, formatiert und an das EreignisArgument e wieder zurückgegeben, mit dem die formatierten Daten
schließlich in das Steuerelement gelangen.
Listing 5.52 FotoFormatieren
Public Sub FotoFormatieren( _
ByVal sender As Object, _
ByVal e As ConvertEventArgs)
' Leeres Datenfeld abfangen
If Not IsDBNull(e.Value) Then
' EventArgument enthält Byte-Array
Dim _img As Byte() = CType(e.Value, Byte())
Dim _ms As New IO.MemoryStream()
' Offset für manche Access Blobs auf 78 setzen
Dim _int As Integer = 0
' Bytes in einen MemoryStream schreiben
_ms.Write(_img, _int, _img.Length - _int)
' Bitmap aus MemoryStream erzeugen
Dim _bmp As New Bitmap(_ms)
_ms.Close()
' Bitmap an EventArgument zurückgeben
e.Value = _bmp
End If
End Sub
382
ADO.NET und Datenbanken
Die Bilddaten erreichen die Prozedur im Objekt e und können als
Array von Bytes ausgelesen werden. Ziel der Prozedur ist es, dieses
Byte-Array in ein Bitmap-Objekt umzuwandeln. Dazu bietet es sich
an, die Bytes in einen MemoryStream zu schreiben und daraus ein
Bitmap-Objekt zu erzeugen, das am Ende wieder an e übergeben
wird.
Die Methode, mit der die Bytes in einen MemoryStream geschrieben
werden, akzeptiert die Argumente offset und count, mit denen
gesteuert werden kann, bei welchem Byte die Übertragung in den
Datenstrom beginnen soll und wie viele Bytes überhaupt übertragen
werden sollen. Die Bilder aus der SQL Server- und der Access-Variante
der Beispieldatenbank dbWürzen benötigen keine Angabe eines Offset-Wertes. Bei manchen Bildern, die in anderen Access-Datenbanken
als OLE-Objekte eingefügt wurden, ist allerdings die Angabe von offset=78 erforderlich, um die Bilddaten von einem proprietären OLEDatenheader zu trennen.
MemoryStream
Falls die Write-Methode mit dem angegebenen Offset-Wert fehlschlägt, müssen Sie das Byte-Array eines Bildes bei der Übertragung
analysieren. Setzen Sie dazu in der Zeile mit _ms.Write einen Haltepunkt und starten Sie die Anwendung im Debugmodus. Wenn der
Haltepunkt erreicht ist, wählen Sie aus dem Kontextmenü von _img
den Eintrag Schnellüberwachung. Der Beginn der eigentlichen Bitmapdaten ist an der Kennung BM zu erkennen, die als Bytes die Werte 66
77 hat. Passen Sie über die Variable _int den Offset-Wert mit der
Position des ersten Bytes der Kennung an.
Nach dem Start der Anwendung und dem Laden der Daten wird das
erste Bild geladen. Bei einer Navigation in den Hauptdaten wechselt
das Bild allerdings nicht, da den BindingManager-Objekten der beiden Tabellen noch die Synchronisation fehlt.
Position synchronisieren
Wählen Sie aus dem linken Codenavigations-Dropdown bmbGewürze,
dann aus dem rechten PositionChanged, um die Ereignisprozedur für
die Navigation in der Haupttabelle anzulegen.
Datenbindung und Datennavigation
383
Abbildung 5.39 Anzeige von Bildern aus der Datenbank
Navigationsereignis Position
Changed
Legen Sie fest, dass beim Wechsel der Position-Eigenschaft der
Hauptdaten der BindingManager der Detaildaten auf dieselbe Position weist.
Listing 5.53 Ereignisprozedur für den Wechsel der Position
Private Sub bmb_PositionChanged( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles bmbGewürze.PositionChanged
' Aktuelles Bild anzeigen
bmbBilder.Position = bmbGewürze.Position
' Bild ist dann vorhanden, wenn beide
' BindingManager auf denselben Datensatz zeigen
If bmbGewürze.Current.row.Item("pkGewürz") <> _
bmbBilder.Current.row.Item("fkGewürz") Then
PictureBox1.Visible = False
Else
PictureBox1.Visible = True
End If
End Sub
Da nicht jedem Gewürz ein Bild zugeordnet ist, sollte die
PictureBox1 nur dann sichtbar sein, wenn die BindingManager beider Tabellen auf denselben Datenschlüssel in der Hauptdatentabelle
zeigen.
384
ADO.NET und Datenbanken
Neues Bild laden
Der Vollständigkeit halber folgt der Code zum Laden eines neuen Bildes, das durch Klick auf btnLaden ausgelöst wird.
Fügen Sie der Maske in der Entwurfsansicht aus der Toolbox ein
OpenFileDialog-Objekt hinzu, über dessen Filter-Eigenschaft Sie in
der Code-Ansicht den zu wählenden Dateityp vorgeben können.
Listing 5.54 Das Laden eines neuen Bildes über einen Datei-Öffnen-Dialog
Private Sub btnBildLaden_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnBildLaden.Click
' Bitmap mit Dateidialog auswählen
With OpenFileDialog1
.CheckFileExists = True
.Filter = "Datendateien (*.jpg)|*.jpg"
If .ShowDialog = DialogResult.OK Then
' Bitmap-Datei as Stream laden
Dim _st As System.IO.Stream = .OpenFile
Dim _img As New Bitmap(_st)
PictureBox1.Visible = True
PictureBox1.Image = _img
End If
End With
End Sub
Nach der erfolgreichen Auswahl einer Bilddatei wird diese von der
OpenFile-Methode als Datenstrom geöffnet und daraus ein BitmapObjekt erzeugt. Die Zuweisung der Bitmap an das Steuerelement
PictureBox1 fügt dieses über die Datenbindung auch in die Datentabelle ein und wird über Aktualisieren in die Datenbank übertragen.
5.11 Hierarchische Daten
Wie in Abschnitt 5.8, »Tabellenverknüpfungen«, gezeigt wurde, ist es
nicht schwer, ein DataGrid an verknüpfte Tabellen zu binden, um in
diesen zu navigieren. Die Darstellung von Datenhierarchien in einem
DataGrid ist jedoch sehr eingeschränkt, da jeweils nur die Daten einer
Tabelle anzeigt werden und kein Überblick über alle untergeordneten
Datensätze in einer Hauptdatentabelle möglich ist.
Hierarchische Daten
Verknüpfte
Tabellen im
DataGrid
385
Herunterladen