677.book Page 451 Thursday, December 5, 2002 2:26 PM Programmieren von XML mit Visual Basic .NET 452 459 462 471 479 495 Web-Releases von SQL Server Überblick über XML-Technologien Generieren von XML-Dokumenten mit dem .NET Framework Dynamische Angabe von XML-Resultsets Das Zusammenspiel von XML und DataSets Erstellen von HTML-Seiten mit XSLT Das Hauptziel dieses Kapitel ist es, ein Verständnis für die Verwendung von XML-Dokumenten in Visual Basic .NET zu vermitteln. Dieses Kapitel setzt grundlegende Kenntnisse zu den XML-Datenformaten, -Schemas und den dazugehörigen Technologien, wie XPath (XML Path Language) und XSLT (Extensible Stylesheet Language Transformation), voraus. Wenn Sie grundlegende Fragen zu diesen Themen haben, wäre nun die Zeit, zu Kapitel 6 zurückzukehren. Dieses Kapitel umfasst Abhandlungen zur Formatierung von XML-Dokumenten, sowie Beispiele, die die Verwendung von XML-Dokumenten in Webanwendungen verdeutlichen, als auch Referenzen für das weiterführende Studium. Der erste Schwerpunkt in diesem Kapitel dreht sich um Erläuterungen zu XML. Der zweite Hauptschwerpunkt liegt insbesondere auf der Betonung der Verwendung von XML mit SQL Server 2000Datenbanken. Zwar werden einige Techniken in diesem Kapitel demonstriert, die auch in Verbindung mit anderen Datenbankquellen verwendet werden können, insbesondere solchen, die sich auf ADO.NET-Datasets stützen, doch wurden die Beispiele nur für den Einsatz mit SQL Server 2000 getestet und evaluiert. Der dritte, dieses Kapitel charakterisierende Schwerpunkt dreht sich um die Web-Releases von SQL Server 2000. Das Kapitel 6 behandelt die Web-Releases 1 und 2. Dieses Kapitel rückt nun das Web-Release 3 (SQLXML 3.0) relativ zu den beiden früheren Versionen ins richtige Licht. Darüber hinaus werde ich auf zwei Innovationen näher eingehen (SQLXML-verwaltete Klassen und Diffgramme), die mit Web-Release 2 vorgestellt wurden und die für .NET-Entwickler von besonderer Bedeutung sind. Es ist Microsofts erklärte Zielstellung, SQL Server 2000 durch eine Reihe von Web-Releases auf dem Stand der neuesten XML-Entwicklungen zu halten. Die mit den Web-Releases vorgestellten Innovationen beeinflussen wesentlich Ihre Fähigkeiten, um Visual Basic .NET zur Verarbeitung von SQL Server-Datenquellen mit XML zu verwenden. Nach einer Einführung zu den Web-Releases und den XML-Technologien aus dem .NET Framework präsentiert dieses Kapitel eine Reihe von Beispielen in vier Abschnitten. Die erste Gruppe von Beispielen dreht sich um die Erstellung von XML-Dokumenten mit SQL-Abfragen und kommentierten XML-Schemata. Der zweite Satz von Beispielen erweitert die verfügbare Funktionalität, um Eingaben zur Laufzeit zu unterstützen. Darüber hinaus demonstriert dieser zweite Abschnitt den Einsatz 451 677.book Page 452 Thursday, December 5, 2002 2:26 PM von XPath-Abfragen, um XML-Dokumente zurückzugeben, und vermittelt alternative Möglichkeiten, um äquivalente Ergebnisse mit SQL-Abfragen zurückzugeben. Der dritte Abschnitt untersucht insbesondere das Zusammenspiel zwischen ADO.NET-Datasets und XML-Dokumenten. Dieser Abschnitt untersucht außerdem Möglichkeiten zur Ausführung erweiterter XPath-Abfragen. Zwei Beispiele aus diesem Abschnitt demonstrieren, wie XML-Dokumente mit hierarchischen DataSets verarbeitet und wie Änderungen an einer Remotedatenbank von SQL Server vorgenommen werden. Der abschließende Abschnitt dieses Kapitels behandelt dann die Verwendung von XSLT, um Webseiten mit HTML-Tabellen auf der Grundlage von XML-Dokumenten vorzubereiten, die Sie mit Visual Basic .NET für eine SQL Server-Datenquelle erstellen können. Sämtliche Beispiele aus diesem Kapitel, mit Ausnahme eines einzigen Beispiels, sind in Module1 aus dem XMLSamples-Projekt zu finden. Diese Beispiele demonstrieren Visual Basic .NET-Code für die Verwaltung von XML-Dokumenten bei der Arbeit mit SQL Server-Datenquellen. Lesen Sie weitere Kapitel für eine allgemeinere Abhandlung der Programmierung mit Visual Basic .NET (beispielsweise werden Windows Forms und Steuerelemente in Kapitel 9 behandelt). Sie können die Beispiele zu diesem Kapitel ausführen, wenn Sie die Kommentarzeichen von den Zeilen aus der Prozedur main entfernen, die die jeweiligen Prozeduren aufrufen. In einigen Fällen müssen Sie das Kommentarzeichen von mehr als einer Zeile entfernen. In den Beschreibungen der Beispiele werden die Zeilen explizit angegeben, deren Kommentarzeichen entfernt werden müssen. Das einzige Beispiel, das sich nicht im XMLSamples-Projektordner befindet, trägt den Namen XMLWebSample. Dieses ASP.NETProjekt umfasst zwei Ordner. Der eine Ordner ist für das Verzeichnis \WWWRoot unter \Inetpub auf Ihrem Webserver bestimmt, das andere Verzeichnis gehört in das Verzeichnis, in dem Sie Ihre Visual Basic .NET-Projektordner ablegen. Beide Ordner dieses ASP.NET-Projektes tragen den Namen XMLWebSample. Web-Releases von SQL Server Wenn Sie XML mit SQL Server 2000 verwenden wollen, sollten Sie definitiv das aktuellste WebRelease in Erwägung ziehen. Zum Zeitpunkt der Erstellung dieses Kapitels handelte es sich dabei um das Web-Release 3. Microsoft unterstützt weiterhin alle Web-Releases in vollem Umfang. Dieser Abschnitt baut auf den früheren Abhandlungen zu den Web-Releases und den allgemeinen XMLFähigkeiten von SQL Server 2000 auf (siehe Kapitel 6). Darüber hinaus stellt dieser Abschnitt Web-Release 3 vor, welches in Kapitel 6 nicht behandelt wurde. In diesem Abschnitt werde ich außerdem zwei wichtige XML-Technologien für die .NET-Entwicklung (SQLXML-verwaltete Klassen und Diffgramme) präsentieren, die im Lieferumfang von Web-Release 2 enthalten sind, aber bisher in diesem Buch noch nicht behandelt wurden. Überblick über das erste und zweite Web-Release Lesen Sie Kapitel 6 für eine genaue Abhandlung zum ersten und zweiten Web-Release für SQL Server 2000. Das erste Web-Release für SQL Server 2000, welches am 15. Februar 2001 freigegeben wurde, stellte zum ersten Mal Updategramme vor. Diese XML-basierte Technologie gibt Entwicklern die Möglichkeit, SQL Server-Datenbanken mittels HTTP über Webverbindungen programmatisch zu verarbeiten. Die Datenverarbeitung ist zwar über Webverbindungen möglich, diese Fähigkeit ist allerdings von Standardsicherheitsregeln abhängig. Darüber hinaus stellte Web-Release 1 eine Möglichkeit für die Übernahme von XML-Dokumenten in eine SQL Server-Datenbank en gros vor. Web-Release 2 (SQLXML 2.0) wurde am 15. Oktober 2001 vorgestellt. Dieses Update für SQL Server 2000 brachte weitere allgemeine XML-Features, wie die clientseitige Formatierung, sowie eine Reihe 452 Kapitel 12 677.book Page 453 Thursday, December 5, 2002 2:26 PM von Fähigkeiten, die explizit auf .NET-Entwickler ausgerichtet sind – SQLXML-verwaltete Klassen und Diffgramme. Mit SQLXML-verwalteten Klassen können die Entwickler die Vorteile der WebReleases direkt in der .NET-Umgebung nutzen. Sie können diese Klassen statt der entsprechenden ADO.NET-Klassen verwenden. Um ein Beispiel zu geben, Sie können statt der SqlDataAdapterKlasse von ADO.NET die SqlXMLAdapter-Klasse von SQLXML einsetzen. Zusätzlich zur größeren Leistungsvielfalt ist die Syntax von SQLXML-verwalteten Klassen leichter beherrschbar. Das zweite neue Feature von besonderem Gewicht für .NET-Entwickler sind Diffgramme. Diese Art von XMLDokumenten ermöglicht die Aktualisierung von SQL Server-Datenbanken direkt aus dem .NET Framework heraus. In Kapitel 6 finden Sie eine Abhandlung aller Erweiterungen, die mit Web-Release 2 vorgestellt wurden. Überblick über Web-Release 3 (SQLXML 3.0) Web-Release 3 ist Bestandteil des Microsoft SQL Server 2000 Web Services Toolkit. Web-Release 3 ist als eigenständiges Produkt seit dem 9. Februar 2002 verfügbar. Das Toolkit wurde etwas später (14. Februar 2002) freigegeben. Das wichtigste Feature des Microsoft SQL Server 2000 Web Services Toolkits ist dessen Fähigkeit, gespeicherte Prozeduren von SQL Server als XML-Webdienste bereitzustellen. In Kapitel 13 wird auf diesen Aspekt des Toolkits genauer eingegangen, denn dieses Kapitel behandelt die Programmierung von XML-Webdiensten. Es gibt zwei URLs zum Herunterladen von Web-Release 3. Wenn Sie die Hauptfunktionen des Web-Releases 3 benötigen, ohne die speziellen Features, die mit dem Microsoft SQL Server 2000 Web Services Toolkit vorgestellt wurden, können Sie das Web-Release mit folgendem URL herunterladen: http://msdn.microsoft.com/downloads/ default.asp?URL=/downloads/sample.asp?url=/MSDN-FILES/027/001/824/msdncompositedoc.xml. Wenn Sie alle Features des Toolkits benötigen, können Sie auf die Webseite mit folgendem URL zugreifen. Diese Site stellt eine viel größere Datei für den Download bereit, als das grundlegende WebRelease 3 (12, 3 MB im Vergleich zu 2,7 MB), sowie eine Verknüpfung zum Microsoft SQAP Toolkit, welches Sie benötigen, um XML-Webdienste veröffentlichen zu können. Die Site beinhaltet darüber hinaus Verknüpfungen zu Webcasts und technischen Dokumenten, die hilfreich sein könnten: http:// msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp?url=/MSDN-FILES/ 027/001/872/msdncompositedoc.xml&frame=true. HINWEIS: Ich habe die Beispiele zu diesem Kapitel mit der Version entwickelt, die alle Features des Toolkits umfasst. Auch wenn Sie nur die einfache Version des Web-Releases 3 herunterladen, empfehle ich Ihnen dringend den Download des SOAP Toolkits 2.0, welches ebenfalls über eine Verknüpfung auf der gleichen Seite wie das Web-Release heruntergeladen werden kann. Die Beispiele aus Kapitel 13 erfordern SOAP (Sie sollten das also gleich alles in einem Aufwasch erledigen). Neben der Fähigkeit, XML-Webdienste zu veröffentlichen, umfasst das Web-Release 3 die gleichen Fähigkeiten, wie die früheren Web-Releases, mit einer Reihe gewisser Verbesserungen und Bugfixes. Beispielsweise unterstützt Web-Release 3 parentID-Vermerke für Diffgramme. Die verwaltete Klasse SqlXmlAdapter verwendet diese Vermerke während der Ausführung von Datenverarbeitungsaufgaben für eine SQL Server-Datenbank aus einer Visual Basic .NET-Anwendung heraus. Viele der Verbesserungen stehen über virtuelle IIS-Verzeichnisse für SQL Server zur Verfügung. Web-Release 3 beinhaltet die Fähigkeit, virtuelle Verzeichnisse die mit früheren Web-Release-Versionen erstellt wurden, aktualisieren zu können. Ich empfehle Ihnen die Installation von Web-Release 3, wenn Sie keine Anwendungen erstellt haben, die mit einem der beiden früheren Web-Releases ausgeführt werden. Schließlich umfasst Web-Release 3 die beiden früheren Web-Releases (zusammen mit den Verbesserungen, die mit Web-Release 3 vorgestellt wurden). Wie für neue Software typisch, ist Web-Release 3 mit den vorhergehenden Versionen auf Grund von geringfügigen funktionellen VerProgrammieren von XML mit Visual Basic .NET 453 677.book Page 454 Thursday, December 5, 2002 2:26 PM besserungen und Bugfixes nicht vollständig abwärtskompatibel. Wenn Sie also Anwendungen erstellt haben, die mit einem der beiden früheren Web-Releases ausgeführt werden, sollten Sie diese Lösungen vor der Installation von Web-Release 3 testen. Eine attraktive Option für diejenigen, die Anwendungen mit früheren Web-Releases erstellt haben, ist die Möglichkeit, Web-Release 3 neben den früheren Web-Releases ausführen zu können. Dies ist möglich, denn die Installation von Web-Release 3 (auch als SQLXML 3.0 bezeichnet) überschreibt keine Dateien der früheren Web-Releases. Einschränkungen und spezielle Fragen werden im Abschnitt Understanding the Side-by-Side Installation Issues (und den nachfolgenden drei Abschnitten) aus dem Thema About This Release in der Dokumentation von Web-Release 3 erörtert. Sie können diese Dokumentation aus der Programmgruppe SQLXML 3.0 im Windows-Startmenü öffnen. HINWEIS: Wenn Ihnen nur eine Testumgebung zur Verfügung steht, sollten Sie die virtuellen IISVerzeichnisse von Web-Release 1 und Web-Release 2 mit dem Dienstprogramm IIS Virtual Directory Management for SQLXML 3.0 aktualisieren. Doppelklicken Sie im Dienstprogramm IIS Virtual Directory Management for SQLXML 3.0 auf das Stammverzeichnis der alten virtuellen Verzeichnisse. Wechseln Sie dann zur Registerkarte Version 3 und klicken Sie auf Aktualisieren auf Version 3. Schließen Sie die Aktualisierung durch Auswahl von Ja und OK ab. SQLXML-verwaltete Klassen Mit SQLXML-verwalteten Klassen können Sie in Ihren Visual Basic .NET-Programmen die Features des zweiten und dritten Web-Releases verwenden. Es gibt drei SQLXML-verwaltete Klassen. Die Namen der Klassen sind SqlXmlCommand, SqlXmlParameter und SqlXmlAdapter. Diese Klassen werden in Visual Basic .NET-Prozeduren verwendet, um Objekte zu erstellen und deren Eigenschaften und Methoden programmatisch zu kontrollieren. Die SQLXML-verwalteten Klassen unterstützen keine Ereignisse. Sie können SQLXML-verwaltete Klassen zwar in .NET Framework-Anwendungen einsetzen, doch gibt es keinerlei Dokumentation zu diesen Klassen in der Hilfe zu Visual Studio .NET oder zu Visual Basic .NET. Die Hilfedatei aus dem Web-Release 3 gibt Auskunft über die Eigenschaften und Methoden, die von den SQLXML-verwalteten Klassen bereitgestellt werden. Darüber hinaus umfasst das Hilfesystem mehrere Codebeispiele, die Sie wahrscheinlich nützlich finden werden. Eine der technischen Dokumentationen (mit dem Titel SQLXML Managed Classes), welche Bestandteil des Microsoft SQL Server 2000 Web Services Toolkit ist, umfasst weitere Hintergrundinformationen, die ich für ein Verständnis zur Verwendung dieser nützlichen Klassen als hilfreich empfunden habe. HINWEIS: Alle an dieser Stelle erwähnten Quellen für Hilfeinformationen zu SQLXML-verwalteten Klassen gehen insbesondere auf die Verwendung der Klassen in C#-Programmen ein und beinhalten Quellcode für die Verwendung der Klassen mit C#. Da das Hilfesystem zum .NET Framework Codebeispiele für Visual Basic .NET als auch für C# bereitstellt, könnte der Eindruck entstehen, dass die SQLXML-verwalteten Klassen nur mit C# funktionieren. Ich kann sämtliche C#-Beispiele in die Visual Basic .NET-Syntax übersetzen. Dieses Kapitel umfasst mehrere Visual Basic .NETBeispiele, die Sie als Anleitung für die Übersetzung von C#-Beispielen verwenden können. Die SqlXmlCommand-Klasse Sie können SqlXmlCommand-Objekte instantiieren, indem Sie eine Verbindungszeichenfolge für das jeweilige Objekt als Bestandteil eines Ausdrucks mit dem New-Operator angeben. Es gibt bei den SQLXML-verwalteten Klassen keine explizite Connection-Klasse. Die Verbindungszeichenfolge muss den SQLOLEDB-Datenprovider angeben, sowie den Server, die Datenbank und einen Sicherheitsmechanismus, wie das auch in ADO und ADO.NET üblich ist. z. B.: 454 Kapitel 12 677.book Page 455 Thursday, December 5, 2002 2:26 PM Dim cmd1 As SqlXmlCommand = New SqlXmlCommand(provider=sqloledb; _ server=Servername; _ database=Datenbankname; _ user id=Benutzeranmeldung; _ password=Benutzerkennwort) Bevor Sie eine SQLXML-verwaltete Klasse verwenden (oder instantiieren) können, müssen Sie in Ihrem Visual Basic .NET-Modul eine Referenz auf den Namespace Microsoft.Data.SqlXml angeben. Sie können diese Referenz in Visual Basic .NET hinzufügen, wenn Sie den Codeeditor öffnen und den Befehl Verweis hinzufügen aus dem Menü Projekt wählen. Wählen Sie im Dialogfeld Verweis hinzufügen auf der Registerkarte .NET aus der Spalte Komponentenname den Eintrag Microsoft.Data.SqlXml aus. Haben Sie auf Ihrer Arbeitsstation sowohl das Web-Release 2 als auch das Web-Release 3 installiert, müssen Sie die passende Microsoft.Data.SqlXml-Version für das WebRelease angeben, die Sie verwenden wollen. Web Release 3 hat die Versionsnummer 3.0.1523.0; die Versionsnummer von Web Release 2 ist 2.0.1125.0. Nach dem Hinzufügen der Referenz zu Ihrem Projekt müssen Sie eine Imports-Anweisung in den Modulen angeben, in denen Sie verwaltete Klassen verwenden wollen, es sei denn, Sie beabsichtigen, den einzelnen Referenzen auf die Klassen den Namespacebezeichner Microsoft.Data.SqlXml voranzustellen. HINWEIS: Es sollte bis zu dieser Stelle in diesem Buch deutlich geworden sein, dass ich mich nicht strikt an Programmierkonventionen halte. Ich glaube, dass bestimmte Umstände – sowohl technischer als auch nicht technischer Natur – wesentlichen Einfluss auf die Einhaltung von Programmierkonventionen haben sollten. Um ein Beispiel zu geben, die Anforderungen an Codebeispiele, die dafür ausgelegt sind, Designfeatures zu illustrieren, sind nicht notwendigerweise die gleichen, wie für Produktivsoftware, die sich auch wieder von Prototypsoftware unterscheiden kann. Ungeachtet dessen werden viele Leser einen Ausgangspunkt mit Richtlinien zur Verwendung von Namespace-Präfixen oder Imports-Anweisungen wünschen. Ziehen Sie die Verwendung von Präfixen in Betracht, wenn Sie für ein bestimmtes Objekt die Aufmerksamkeit ausdrücklich auf die Quelle des Namespaces richten wollen. SqlXmlCommand-Methoden Wie der Name verrät, können SqlXmlCommand-Objekte SQL-Anweisungen für eine Datenbank auf einem SQL Server ausführen und ein Resultset im XML-Format zurückgeben. Beispielsweise generiert die ExecuteStream-Methode ein Resultset für eine SQL-Anweisung und erstellt ein neues Stream-Objekt für das Resultset im XML-Format. Ein Stream-Objekt repräsentiert eine ByteSequenz, z. B. eine Byte-Sequenz für ein XML-Dokument. Bevor Sie mit den Inhalten eines StreamObjektes arbeiten können, müssen Sie dieses typischerweise an einen Reader übergeben, damit der Reader die Ausgabe in einem XML-Dokument speichern kann, welches Sie anzeigen oder als Textzeichen verarbeiten können. Es gibt verschiedene Arten von Stream-Objekten. Die Beispiele aus diesem Kapitel demonstrieren, wie FileStream- und MemoryStream-Objekte funktionieren, welche im Namespace System.IO definiert sind. FileStream-Objekte verweisen auf Dateien im Dateisystem. MemoryStream-Objekte sind Speichervariablen, auf die Sie verweisen können, solange diese gültig sind. Die ExecuteToStreamMethode des SqlXmlCommand-Objektes übergibt das Resultset an ein existierendes Stream-Objekt, statt ein neues Stream-Objekt anzulegen. Sie können SqlXmlCommand-Objekte verwenden, um XmlReader-Objekte mit der ExecuteXmlReader-Methode zurückzugeben. Ein XmlReader-Objekt ist ein Element aus dem System.XmlNamespace. Prozeduren, die XmlReader-Objekte verwenden, können schnell, nicht zwischengespeichert, und nur vorwärts gerichtet auf ein Stream-Objekt mit XML-Daten zugreifen. Ein üblicher Programmieren von XML mit Visual Basic .NET 455 677.book Page 456 Thursday, December 5, 2002 2:26 PM Grund für die Erstellung eines XmlReader-Objektes ist die Auswahl einer Teilmenge von Knoten aus einem XML-Dokument, das mit dem XmlReader-Objekt verknüpft ist. Ein Knoten kann einer Zeile mit Daten entsprechen, die im XML-Dokument enthalten ist. Aus diesem Blickwinkel betrachtet ist die Auswahl einer Teilmenge von Knoten äquivalent zur Verwendung einer WHERE-Klausel in einer SELECT-Anweisung, um eine Teilmenge von Zeilen aus einer Tabelle oder Sicht anzugeben. Sie können außerdem die Methoden CreateParameter und ClearParameters für Instanzen der SqlXmlCommand-Klasse aufrufen. Die CreateParameter-Methode gestattet Ihnen die Angabe eines Parameters für das SqlXmlCommand-Objekt, damit Sie Werte zur Laufzeit festlegen können. Mit dieser Fähigkeit können Ihre Prozeduren Werte für Befehle auf der Grundlage von Benutzereingaben und anderen Aspekten der Arbeitsumgebung dynamisch setzen. Wenn Sie ein SqlXmlCommandObjekt mit anderen Parametern oder ohne Parameter wieder verwenden wollen, müssen Sie die ClearParameters-Methode aufrufen, um alle vorhandenen Parameter zu löschen. Eine letzte Methode vervollständigt die von der SqlXmlCommand-Klasse bereitgestellte Funktionalität. Die ExecuteNonQuery-Methode ist für solche Befehle dienlich, die kein Resultset zurückgeben, z. B. Diffgramme. SqlXmlCommand-Eigenschaften Die möglichen Angaben für die CommandType-Eigenschaft eines SqlXmlCommand-Objektes weisen auf die speziellen Rollen hin, die SqlXmlCommand-Objekte spielen können. Die folgende Aufzählung von CommandType-Einstellungen bestimmt alle möglichen Quellen für ein SqlXmlCommand-Objekt. b SqlXmlCommandType.Sql Weist darauf hin, dass der Befehl eine SQL-Quelle für den Befehl angibt, z. B. eine SELECT-Anweisung mit einer FOR XML-Klausel. b SqlXmlCommandType.XPath Ist angebracht, wenn Sie eine Abfragezeichenfolge mit einem XPath-Ausdruck angeben. b SqlXmlCommandType.TemplateFile Ermöglicht die Ausführung einer Vorlagedatei, die entweder SQL- oder XPath-Syntax beinhaltet. Der Pfad auf die Datei wird in der CommandText-Eigenschaft angegeben. Die CommandText-Eigenschaft verweist als speziell auf die Vorlagedatei. Diese Vorlagedateien haben das gleiche Format wie die Dateien, die in Kapitel 6 angesprochen wurden, diese müssen sich aber nicht in einem virtuellen IIS-Verzeichnis befinden. b SqlXmlCommandType.Template Ermöglicht dem SqlXmlCommand-Objekt die Ausführung des Inhaltes einer Vorlagedatei in SQL- oder XPath-Syntax mit einem Pfad, der in der CommandTextEigenschaft angegeben werden muss. Die CommandStream-Eigenschaft bezeichnet die Parameter zum Öffnen eines FileStream-Objekts mit der Vorlage. Der Parameter SqlXmlCommandType.Template verweist lediglich auf den Pfad und den Dateinamen der Vorlagedatei – aber nicht auf die eigentlichen Inhalte. b SqlXmlCommandType.UpdateGram Gibt ein Updategramm an, das vom SqlXmlCommandObjekt ausgeführt werden soll. b SqlXmlCommandType.Diffgram Bezeichnet ein Diffgramm als Argument des SqlXmlCommand-Objektes. Verwenden Sie die CommandText-Eigenschaft, um die Quelle des SqlXmlCommand-Objektes anzuzeigen. Um ein Beispiel zu geben, die CommandType-Einstellung SqlXmlCommandType.TemplateFile gestattet Ihnen die Verwendung der CommandText-Eigenschaft zur Angabe eines Pfades und eines Dateinamens für die Vorlagedatei. In Visual Basic .NET-Anwendungen brauchen Sie den Text einer Abfrageanweisung nicht auf die gleiche Art und Weise zu schützen, wie bei Webanwendungen und virtuellen IIS-Verzeichnissen. Dies ergibt sich aus der Tatsache, dass die Benutzer Visual Basic 456 Kapitel 12 677.book Page 457 Thursday, December 5, 2002 2:26 PM .NET-Anwendungen mit kompilierten .exe-Dateien ausführen. Normalerweise werden Sie entweder die SQL- oder die XPath-Syntax zur Angabe der CommandText-Eigenschaft mit einer dazugehörigen CommandType-Eigenschaftseinstellung verwenden. Ausgewählte andere SqlXmlCommand-Eigenschaften werden in den Beispielen zu diesem Kapitel verwendet. Wenn Ihr Resultset kein einzelnes übergeordnetes oder Stammelement umfasst, können Sie eines mit der RootTag-Eigenschaft angeben. Wenn Sie eine XPath-Abfrage ausführen, können Sie den Pfad auf die Datei eines Zuordnungsschemas, das mit der Abfrage verknüpft ist, über die SchemaPath-Eigenschaft des SqlXmlCommand-Objekts angeben. Ein Zuordnungsschema kann mit speziellen Kommentaren Beziehungen zwischen den Elementen und Attributen eines Schemas bezeichnen, das ein XML-Dokument und eine SQL Server-Datenquelle darstellt. Die XslPath-Eigenschaft gestattet Ihnen die Angabe eines Dateinamens und eines Pfades auf eine Datei, die die unbearbeitete XML-Ausgabe, die von der CommandText-Eigenschaft spezifiziert wird, in ein anderes Format überführt, z. B. eine HTML-Tabelle. Siehe den Abschnitt SqlXmlCommand Object aus der Dokumentation zum Web-Release 3 für eine Zusammenfassung der SqlXmlCommand-Eigenschaften, die in diesem Kapitel nicht behandelt werden. Die SqlXmlParameter-Klasse SqlXmlCommand-Objekte können über hierarchisch voneinander abhängige Parameter verfügen, die von SqlXmlParameter-Objekten repräsentiert werden. Verwenden Sie die CreateParameterMethode eines SqlXmlCommand-Objektes, um ein SqlXmlParameter-Objekt zu instantiieren. Nach der Instantiierung können Sie den Name- und Value-Eigenschaften des SqlXmlParameter-Objektes Werte zuweisen. Die Name-Eigenschaft bietet eine bequeme Möglichkeit zur Referenzierung des SqlXmlParameter-Objektes und die Value-Eigenschaft ermöglicht Ihnen die Zuweisung eines Wertes an den Parameter zur Laufzeit. Die SqlXmlAdapter-Klasse SqlXmlAdapter-Objekte können für den gleichen Zweck eingesetzt werden, wie SqlDataAdapterObjekte im Namespace System.Data.SqlClient. Nach der Deklarierung einer Variablen als SqlXmlAdapter-Objekte können Sie die Variable mit einem Ausdruck instantiieren, der den New-Operator für die SqlXmlAdapter-Klasse umfasst. Das SqlXmlAdapter-Objekt kann eine Variable als Argument akzeptieren, die auf ein SqlXmlCommand-Objekt verweist. Die Syntax für diese Konstruktion wird an dieser Stelle dargestellt, wobei cmd1 ein zuvor instantiiertes SqlXmlCommand-Objekt repräsentiert. Das Argument cmd1 bezeichnet die Datenquelle, auf die mit dem SqlXmlCommand-Objekt zugegriffen wird. Dim dap1 As SqlXmlAdapter Dap1 = New SqlXmlAdapter (cmd1) SqlXmlAdapter-Objekte verfügen über zwei Methoden namens Fill und Update. Verwenden Sie die Fill-Methode, um ein Dataset zu füllen. Rufen Sie die Update-Methode auf, um in der Datenquelle, auf die das SqlXmlAdapter-Objekt verweist, Zeilen einzufügen, zu modifizieren oder zu löschen. Bei diesen Methoden brauchen Sie nur das Dataset als Argument anzugeben. Es gibt keine Notwendigkeit zur Angabe einer bestimmten Tabelle aus dem Dataset. Ich mag das SqlXmlAdapter-Objekt wegen der einfachen Vorgehensweise, im Vergleich zu SqlDataAdapter-Objekten aus dem Namespace System.Data.SqlClient, mit der ich Datenverarbeitungsaufgaben ausführen kann. Ein paar Beispiele werden später in diesem Kapitel die neue Syntax demonstrieren, mit der die Notwendigkeit der UpdateCommand-Eigenschaft (und in diesem Sinne auch die InsertCommand- und DeleteCommand-Eigenschaften) eliminiert wird. Programmieren von XML mit Visual Basic .NET 457 677.book Page 458 Thursday, December 5, 2002 2:26 PM Diffgramme für die Datenverarbeitung Ein Diffgramm ist ein XML-Format zur Darstellung der Datenwerte in einem Dataset. Das .NET Framework verwendet dieses XML-Format automatisch für die Übermittlung von Daten zwischen einem Client und einer SQL Server-Datenbank. Darüber hinaus können Sie das Diffgrammformat direkt mit SQL Server-Datenbanken verwenden, ähnlich wie Sie Updategramme einsetzen können. In Kapitel 6 finden Sie Beispiele zur Verwendung von Updategrammen für die Datenverarbeitung mit einer SQL Server 2000-Datenbank, um mehr über die Möglichkeiten zu erfahren, die Ihnen Diffgramme in Webanwendungen für SQL Server bieten. Der folgende Code zeigt das allgemeine Layout von Diffgrammen. Beachten Sie, dass dieser mit einer Deklaration beginnt, die das Diffgramm als ein XML-Dokument bezeichnet. Danach werden mehrere Namespaces referenziert. Die für Datenverarbeitungsaufgaben wichtigsten Bereiche aus dem Dokument sind die Abschnitte DataInstance und before. Der DataInstance-Abschnitt bezeichnet den aktuellen Wert aller Zeilen aus der Datenquelle. Um ein Beispiel zu geben, dieser Abschnitt umfasst all diejenigen Zeilen, die über modifizierte Spaltenwerte verfügen, sowie alle eingefügten Zeilen und alle nicht modifizierten Zeilen, die nicht aus der Datenquelle gelöscht wurden. Der before-Abschnitt enthält die ursprünglichen Werte für modifizierte Zeilen. Gelöschte Zeilen werden ebenfalls im before-Abschnitt aufgeführt, aber nicht im DataInstance-Abschnitt. Der errors-Abschnitt ist optional. Dieser Abschnitt umfasst Fehlermeldungen für die Zeilen aus dem DataInstance-Abschnitt. Eine Ansammlung von Attributen unterstützt bestimmte Objektive, z. B. die Zuordnung von Zeilen aus dem DataInstance-Abschnitt zu den entsprechenden Zeilen in den before- und errors-Abschnitten, sowie die Hervorhebung von Zeilen, die an Einfüge-, Aktualisierungs- und Löschaufgaben beteiligt sind. <?xml version="1.0"?> <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <DataInstance> </DataInstance> <diffgr:before> </diffgr:before> <diffgr:errors> </diffgr:errors> </diffgr:diffgram> Wenn Sie ein Visual Basic-Entwickler sind und mit dem .NET Framework arbeiten wollen, wird es Sie freuen, zu hören, dass Sie Diffgramme einsetzen können, ohne deren Format wirklich erlernen zu müssen. Das Kapitel 10 und das Kapitel 11 verdeutlichen beispielsweise, wie Datenverarbeitungsaufgaben mit Windows Forms und ASP.NET-Seiten ausgeführt werden können. In beiden Fällen verwendet ADO.NET Diffgramme im Hintergrund. Ich denke, dass das Verständnis des Formats und des Layouts von Diffgrammen mir hilft, die Gründe für die Syntax zur Angabe von Datenverarbeitungsaufgaben im .NET Framework zu erfassen. Meine beiden bevorzugtesten Ressourcen für die Entwicklung dieses Verständnisses sind: b Der Abschnitt Diffgramme aus der Dokumentation zu Visual Studio .NET. Sie können auf diese Dokumentation über das Startmenü von Windows zugreifen. 458 Kapitel 12 677.book Page 459 Thursday, December 5, 2002 2:26 PM b Der Abschnitt Using DiffGrams to Modify Data aus der Dokumentation zum Web-Release 3. Diese Dokumentation wird mit der Standardversion des Web-Releases 3 mitinstalliert. Sie können auf diese Dokumentation über das Startmenü von Windows zugreifen. Dieses Kapitel umfasst eine Reihe von Beispielen zur Verarbeitung von ADO.NET-Datasets mit SQLXML-verwalteten Klassen. Eines dieser Beispiele betont die Verwendung von Diffgrammen im .NET Framework für Datenverarbeitungsaufgaben. Das zweite Beispiel verdeutlicht, wie einfach es ist, diese ADO.NET-Lösung von einer Windows-Lösung in eine ASP.NET-Anwendung zu konvertieren. Beide Beispiele bestätigen, dass Sie Datenverarbeitungsaufgaben auf der Basis von Diffgrammen ausführen können, ohne mit diesen in Ihrem Code umgehen zu müssen. Die Tatsache, dass Sie Diffgramme ohne eigentliche Programmierung verwenden können, wirft eine interessante Frage über die allgemeine Rolle von XML für typische .NET Framework-Entwickler auf. Es gibt keinen Zweifel, dass für erfahrene und fortgeschrittene .NET Framework-Entwickler ein gutes Verständnis von XML eine Voraussetzung darstellt. Es bleibt aber abzuwarten, bis zu welchem Grad typische Entwickler die sämtliche (oder wenigstens die meisten) Details der XML-Programmiersprachen beherrschen müssen (siehe den nächsten Abschnitt für einen Überblick zu einigen dieser Sprachen). Um ein Beispiel zu geben, es könnte sein, dass XML-Syntax weitestgehend nicht erforderlich ist, wenn Sie XML mit Visual Basic .NET oder mit SELECT-Anweisungen, die eine FOR XML-Klausel umfassen, indirekt verarbeiten. Setzt sich dieser Trend fort, könnte XML einer großen Menge von Funktionen zugrunde liegen, wobei typische Entwickler in der Lage sind, andere, bekanntere Sprachen zu verwenden, um die XML-Konstrukte zu verarbeiten. Dies ähnelt der Vorgehensweise, bei der Visual Basic-Entwickler ADO als Möglichkeit verwendet haben, um OLE DB-Datenprovider zu programmieren. Auf der anderen Seite könnte sich XML als eine notwendige Syntax für „echte“ Programmierer herausstellen. Wenn Sie beispielsweise die Bedeutung abwägen, die XML in Computerpublikationen beigemessen wird, könnten Sie in der Tat leicht zu diesem Schluss kommen. Ich bin mir nicht sicher, wo Sie in diesem Kontinuum enden werden. Jedoch ist klar, dass die Vermeidung von XML-Themen, ein Risiko für Ihre Zukunft als Entwickler darstellt. Überblick über XML-Technologien XML umfasst nicht nur grundlegende Designfragen zu XML-Dokumenten, die Daten enthalten. Dieser Abschnitt stützt sich auf die Abhandlung zu XML-Dokumentformaten und diesbezüglichen Technologien aus Kapitel 6. Darüber hinaus bietet dieser Abschnitt einen Überblick über XML-seitige Aufgaben, die Sie mit dem .NET Framework ausführen können. XML-Datenformate XML-Formate verfügen über mehrere separate Features. b Tags können für bestimmte Dokumente angepasst werden. b Sie können Daten entweder in Elementen oder in Attributen darstellen. b Alle XML-Dokumente beginnen mit einer Deklaration, die die XML-Version des Dokuments bekannt gibt. Diese und weitere Syntaxmerkmale wurden in Kapitel 6 in ausreichendem Maße diskutiert, um den Stoff dieses Kapitel bewältigen zu können. Überfliegen Sie die Abschnitte zu XML-Formaten und XML-Schemata, um sicherzustellen, dass Sie das erforderliche Hintergrundwissen für die Ausführungen im aktuellen Kapitel besitzen. Sie können auch die Website des World Wide Web Consortium besuchen (http://www.w3c.org oder http://www.w3.org), wenn Sie an umfassenden Informationen zu den neuesten öffentlichen XML-Standards interessiert sind. Programmieren von XML mit Visual Basic .NET 459 677.book Page 460 Thursday, December 5, 2002 2:26 PM XML-Dokumente Das XML-Datenformat gewinnt aus vielen Gründen Anerkennung. Einer der wichtigsten Gründe für Datenbankentwickler ist die Fähigkeit von XML-Daten, hierarchische Beziehungen widerzuspiegeln. Die Art und Weise, wie hierarchische Beziehungen in XML-Dokumenten beschrieben werden, unterscheidet sich sehr von den relationalen Datenmodellen, die hierarchische Beziehungen mit Verbindungen zwischen zwei oder mehreren Tabellen darstellen. Ein XML-Dokument kann beispielsweise eine Ansammlung von Bestellungen mit den dazugehörigen einzelnen Posten, also den Detailinformationen, physisch verschachtelt darstellen. In der hierarchischen Repräsentierung, die in XML-Formaten beliebt ist, wird jede Zeile aus der Tabelle mit den Bestellungen nur einmal dargestellt, unabhängig davon, wie viele Posten der jeweiligen Bestellung zugeordnet sind. Im relationalen Modell würde die Ansammlung der Bestellungen hingegen als eine einzelne, flache, virtuelle Tabelle mit den Daten aus der Posten-Tabelle, die den Daten aus der Bestellungstabelle zugeordnet sind, dargestellt. Identische Spaltenwerte aus der Tabelle mit den Bestellungen würden bei einer relationalen Repräsentierung in der virtuellen Tabelle für jeden Posten wiederholt werden. Eine weitere sehr wichtige Charakteristik der XML-Formatierung ist die Repräsentierung der Daten als Text. Dies bedeutet, dass Sie (und andere Personen) die Daten ohne zusätzliche spezielle Umsetzungen lesen können. Frühere Datenformate verwendeten typischerweise ein binäres Datenformat, wodurch die Daten des Dokuments nicht unmittelbar lesbar dargestellt wurden und darüber hinaus schlechter über Firewalls transportiert werden konnten. Es gibt zwar ein funktionsreiches Programmiermodell für die Verarbeitung von XML-Datendokumenten, es ist aber wichtig zu verstehen, dass XML-Dokumente nur Textdokumente sind. Es ist also möglich, übliche Textanalysemethoden zu verwenden, um ausgewählte Daten aus einem XML-Dokument zu extrahieren. Ich werde eine benutzerdefinierte Analysetechnik später in diesem Kapitel beschreiben. XML-Schemata Zusätzlich zu den Daten aus XML-Dokumenten werden Sie häufig mit dem Schema von XMLDokumenten arbeiten. Schemata von XML-Dokumenten dienen ähnlichen Aufgaben wie die Schemata von Datenbanken. Insbesondere beschreibt ein Schema die Datenelemente und die Beziehungen zwischen den Ansammlungen von Datenelementen. Sie können das .NET Framework sowohl als Hilfe für die Erstellung neuer Schemata verwenden, als auch für die Erstellung von Schemata für existierende Dokumente. Dieses Kapitel konzentriert sich exklusiv auf Schemata im XSD-Format. XSD ist der aktuelle Standard für die Repräsentierung der Struktur eines Dokumentes. Bei der ersten Freigabe von SQL Server verwendete Microsoft das XDR-Format für die Festlegung der Strukturen von Dokumenten. Als die Entscheidung für das XDR-Format fiel, gab es noch keinen universell anerkannten Standard, wie XSD, für die Repräsentierung der Struktur eines Dokumentes. Microsoft hat ein XSLT-Stylesheet für die Übersetzung von Schemata im XDR-Format in das entsprechende XSD-Format veröffentlicht. Sie können dieses Stylesheet unter http://msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp?url=/MSDN-FILES/027/001/539/msdncompositedoc.xml finden. Als ein .NET-Entwickler, der mit SQL Server arbeitet, werden Sie XML-Dokumente häufig für SQL Server-Datenquellen erstellen wollen. In diesem Fall kann das .NET Framework eine Darstellung des Schemas für die XML-Dokumente vom Schema der SQL Server-Datenquelle, welche die Werte für das XML-Dokument bereitstellt, ableiten. In der Tat können Sie ADO.NET-Objekte verwenden und Diffgramme oder ein XSD-Schema indirekt mit dem .NET Framework aufbauen. Einer der Gründe für die manuelle Erstellung eigener Schemata ist die Erstellung eines stark typisierten DataSets. Diese Art von Dataset kann sich wie eine benutzerdefinierte Klasse verhalten, mit der Ausnahme, dass es die Eigenschaften, Methoden und Ereignisse von ADO.NET-Datasets erbt. Geben 460 Kapitel 12 677.book Page 461 Thursday, December 5, 2002 2:26 PM Sie das Schema für die Klasse zur Entwurfszeit an. Das Schema definiert die Struktur des stark typisierten DataSets. Sie können dann jederzeit eine Instanz des DataSets mit dem gleichen New-Operator instantiieren, den Sie auch für die Instantiierung andere Objekte verwenden. Sie können das typisierte Dataset mit einem SqlDataAdapter-Objekt füllen. Stark typisierte DataSets haben einen deutlichen Vorteil gegenüber den Standard-DataSets die .NET für Sie erstellen kann. Um ein Beispiel zu geben, Sie können sich explizit auf die Spalten über deren Namen beziehen, statt deren Spaltenposition innerhalb eines DataTable-Objektes im Dataset angeben zu müssen. Siehe das Thema »Arbeiten mit typisierten DataSets« und dessen Verknüpfungen in der Dokumentation von Visual Studio .NET für weitere Details zu diesem DataSet-Typ, Anweisungen zu dessen Erstellung und Codebeispielen zu dessen Verwendung. Ein kommentiertes Schema ist ein spezieller Typ eines Schemas für die Angabe der Struktur eines XML-Dokumentes, wobei Sie gleichzeitig eine externe Quelle für die Struktur angeben. Statt mit dem .NET Framework ein Schema implizit aufzubauen, können Sie dieses explizit erstellen. Das Thema Using Annotations in XSD Schemas aus der Dokumentation zum Web-Release 3 umfasst viele hilfreiche Verknüpfungen für eine genauere Abhandlung der manuellen Techniken, die Sie verwenden können, um kommentierte Schemata zu erstellen. Ein allgemeines Verständnis dieses Themas in Verbindung mit den zahlreichen Beispielen aus der .NET Framework-Dokumentation und den Beispielen aus diesem Buch kann Ihnen dabei helfen, kommentierte Schemata zu lesen und für benutzerdefinierte Erweiterungen in Ihren Anwendungen anzupassen. Zwei übliche Einsatzgebiete für kommentierte Schemata im .NET Framework sind der Aufbau von XML-Dokumenten anhand einer Datenbank und die Benennung von Spalten in einem XML-Dokument mit Namen, die sich von der zugrunde liegenden Datenquelle unterscheiden. XPath-Abfragen XPath ist eine Sprache, die Ihnen den Zugriff auf die Bereiche eines XML-Dokumentes ermöglicht. Sie können XPath verwenden, um ein XML-Dokument abzufragen, ähnlich wie Sie SQL verwenden, um eine Datenbank abzufragen. Ein XPath-Abfrageausdruck kann eine Auswahl aus Dokumentabschnitten oder -typen treffen, beispielsweise aus den Elementen, Attributen und dem Text eines Dokumentes. Sie können Knoten für übergeordnete, untergeordnete und gleichartige Elemente eines angegebenen Dokumententyps angeben. Ein übergeordneter Knoten ist ein Typ, der den aktuellen Typ beinhaltet. Um ein Beispiel zu geben, eine Bestellung ist ein übergeordnetes Element von einzelnen Posten aus der Bestellung. Im umgekehrten Sinne ist ein untergeordnetes Element ein Element des aktuellen Typs. Jeder Typ, den Sie in der Auswahl einer XPath-Abfrage verwenden, kann einen Satz von Knoten zurückgeben. Diese Knoten entsprechen generell den Zeilen aus dem Resultset einer SQL-Anweisung, allerdings ist die Syntax zur Angabe des XML-Dokumenttyps mit einer XPath-Abfrage komplett anders aufgebaut, als die traditionelle SQL-Syntax. Sie können SQLXML-verwaltete Klassen verwenden, um XPath-Abfragen zu formulieren und auszuführen. Sie können Abfrageanweisungen sogar dynamisch zur Laufzeit erstellen. Der Satz von Knoten, der von der XPath-Abfrage zurückgegeben wird, ist in einem XmlNodeList-Objekt enthalten. Das .NET Framework gibt Visual Basic-Entwicklern die Möglichkeit, die einzelnen Knoten aus der Liste der Knoten zu durchlaufen, um die Ergebnisse des XPath-Abfrageausdrucks zu untersuchen. Mehrere Codebeispiele im übrigen Teil dieses Kapitels demonstrieren die Syntax für die Ausführung dieser Art von Aufgaben. In Kapitel 6 finden Sie eine weitere Abhandlung der XPathSprache und zusätzliche Ressourcen zu deren Erlernung. Programmieren von XML mit Visual Basic .NET 461 677.book Page 462 Thursday, December 5, 2002 2:26 PM XSLT-Formatierung XSLT ermöglicht die programmatische Umwandlung von Dateien im XML-Format in verschiedene andere Formate. XSLT hat viele potentielle Einsatzgebiete. Dieses Buch behandelt jedoch nur die Fähigkeit von XSLT zur Umwandlung von XML-Dateien in HTML-Tabellen auf einer Webseite. Sie können XSLT-Transformationen in .NET-Anwendungen mit SQLXML-verwalteten Klassen bewerkstelligen. Eine .xslt-Datei ist eine separate Datei. Eine .NET-Anwendung kann sich auf diese .xsltDatei über eine Eigenschaftszuweisung an ein SqlXmlCommand-Objekt beziehen. Eine .xslt-Datei kann Stylesheet-Elemente, HTML-Code und Verarbeitungsanweisungen für die Extrahierung der Inhalte aus dem XML-Dokument enthalten. Eine .xslt-Datei ist ein Dateityp, den sehr wahrscheinlich eher Webentwickler als Visual Basic-Entwickler erstellen werden. Mit ausreichendem Vorbedacht und Zusammenarbeit kann der Webentwickler den Visual Basic-Entwicklern einen Satz von .xslt-Standarddateien zur Verfügung stellen. Wurde eine .xslt-Datei bereitgestellt, kann der Visual Basic-Entwickler diese unmittelbar referenzieren, um die Daten für eine Webseite zu formatieren. Das heißt, der Visual Basic-Entwickler kann mithilfe einer .xslt-Datei eine HTML-Datei direkt erstellen. Der Visual Basic-Entwickler kann die SqlXmlCommand-Klasse verwenden, um Zeilen aus einer SQL Server-Datenquelle im XML-Format zu extrahieren. Danach kann der Entwickler eine Eigenschaftszuweisung für die SqlXmlCommand-Klasse vornehmen, um die Formatierung der Zeilen für die Anzeige auf einer Webseite in einer HTML-Tabelle zu aktivieren. Die Webseite ist statischer Natur. Allerdings kann das .NET Framework den Benutzern über den Aufruf eines Programms zur Erstellung statischer Seiten auf Anforderung die Erstellung von Inhalten im Webformat gestatten. Generieren von XML-Dokumenten mit dem .NET Framework Dieser Abschnitt demonstriert Methoden für die Erstellung und Ablage von XML-Dokumenten auf der Grundlage von SQL Server-Datenquellen. Die Beherrschung der Konzepte für die Umsetzung dieser Art von Aufgaben bringt Sie mit den Techniken für die Arbeit mit XML-Inhalten in Visual Basic .NET-Anwendungen in Kontakt. Da XML in vielen Einsatzbereichen wichtig ist, einschließlich im Bereich der Veröffentlichung von Inhalten als HTML, ist es wichtig, dass Sie diese Techniken erlernen. Für viele der Beispiele aus diesem Kapitel werden Sie eine Referenz auf den Namespace Microsoft.Data.SqlXml hinzufügen müssen. Lesen Sie den Abschnitt »SQLXML-verwaltete Klassen« in diesem Kapitel für detaillierte Anweisungen zum Hinfügen einer Referenz auf den Namespace Microsoft.Data.SqlXml. Darüber hinaus gehen einige Beispiele von der Existenz einer ImportsAnweisung für diesen Namespace und für andere ausgewählte Namespaces aus. Ich habe sämtliche Beispiele aus diesem Kapitel, mit Ausnahme eines Beispiels, mit den folgenden Imports-Anweisungen am Anfang des Moduls entwickelt. Bei der Untersuchung der einzigen Ausnahme werde ich bei deren Beschreibung spezielle Anweisungen zur Einrichtung der Umgebung geben. Imports Imports Imports Imports 462 Microsoft.Data.SqlXml System.Data.SqlClient System.Xml System.IO Kapitel 12 677.book Page 463 Thursday, December 5, 2002 2:26 PM Erstellen eines XML-Dokuments mit T-SQL Eine der natürlichsten Möglichkeiten für SQL Server-Entwickler zur Erstellung von XML-Dokumenten besteht in der Verwendung einer SQL-Anweisung. Wie bereits in Kapitel 6 erwähnt, können Sie mit SQL und der FOR XML-Klausel XML-Fragmente erstellen (wenn das Resultset mehr als eine einzelne Zeile umfasst). Eigentlich stellen diese Fragmente fast komplette XML-Dokumente dar, mit der Ausnahme des Stammelementes. Eine Strategie zur Erstellung eines Resultsets in Form eines XML-Dokumentes ist also die Ausführung einer SQL-Anweisung mit einer FOR XML-Klausel und die Deklarierung eines Stammelementes. Da eine SQL-Anweisung nichts weiter ist, als ein Befehl für eine SQL Server-Instanz, können Sie ein SqlXmlCommand-Objekt verwenden, um den Befehl auszuführen und ein XML-Dokument zurückzugeben. Das SqlXmlCommand-Objekt verfügt über mehrere Features, die sich besonders für den Zugriff auf eine SQL Server-Datenquelle und die Rückgabe eines XML-Dokuments eignen. Der Konstruktor des SqlXmlCommand-Objektes akzeptiert eine direkte Verbindungszeichenfolge. Dies bedeutet, dass es keine Notwendigkeit gibt, ein separates Verbindungsobjekt instantiieren zu müssen, wenn Sie lediglich einen Befehl ausführen wollen. Weiterhin können Sie mit der RootTag-Eigenschaft des SqlXmlCommand-Objektes ein Stammelement angeben, um das von der SQL-Anweisung mit der FOR XML-Klausel zurückgegebene XML-Fragment in ein vollständiges XML-Dokument zu konvertieren. Zwei weitere Eigenschaften gestatten Ihnen die Vervollständigung der SQL-Spezifikation für das SqlXmlCommand-Objekt. Geben Sie als CommandType-Eigenschaft SqlXmlCommandType.Sql an, um anzuzeigen, dass Ihr SqlXmlCommand-Objekt eine SQL-Anweisung ausführt. Weisen Sie dann die SQL-Anweisung der CommandText-Eigenschaft zu. Nun kann die ExecuteToStreamMethode des SqlXmlCommand-Objekts das XML-Dokument als eine Sequenz von Bytes zurückgeben. Die nun folgende Prozedur SaveDBQueryAsXmlToFile illustriert die Syntax für die Erstellung einer Datei, die ein XML-Dokument auf der Grundlage einer SQL-Anweisung enthält. Die Prozedur beginnt mit der Angabe einer Verbindungszeichenfolge für ein SqlXmlCommand-Objekt. Da dieses Objekt eine Instanz einer SQLXML-verwalteten Klasse mit gleichem Namen darstellt, muss in der Verbindungszeichenfolge der SQLOLEDB-Datenprovider angegeben werden. Der Konstruktor des SqlXmlCommand-Objektes referenziert diese Verbindungszeichenfolge, um das Objekt zu instantiieren. Im nächsten Codeblock des Beispiels werden dann die Eigenschaften des SqlXmlCommandObjektes zur Rückgabe eines XML-Dokumentes gesetzt. Insbesondere weist die CommandTextEigenschaft darauf hin, dass das Dokument alle Zeilen aus der Shippers-Tabelle enthalten wird. Die RootTag-Eigenschaft bezeichnet die Zeichenfolge Shippers für die Verwendung als Stammelement des Dokumentes. Wie Sie erkennen können ist die Übergabe der zurückgelieferten Informationen vom SqlXmlCommand-Objekt an eine Datei ein aus mehreren Schritten bestehender Prozess. Bevor die ExecuteToStream-Methode des SqlXmlCommand-Objekts aufgerufen wird, müssen Sie einen Namen und einen Pfad für die Datei angeben, in der das vom SqlXmlCommand-Objekt generierte XML-Dokument gespeichert werden soll. In der Prozedur wird diese Anforderung mit einer Zuweisung der Zeichenfolge namens myXMLfile erfüllt. Dem zugewiesenen Wert entspricht der Pfad und der Dateiname des Dokuments. (Sie sollten einen anderen Pfad auf die Datei angeben, wenn der Pfad auf Ihrem Computer nicht gültig ist.) Als nächstes instantiiert die Prozedur ein FileStream-Objekt, um das XML-Dokument zu speichern. Der Kontruktor erwartet zwei Argumente. Das eine Argument ist eine Zeichenfolgenvariable, myXMLfile, die den Namen und den Pfad auf die Datei angibt. Das zweite Argument weist darauf hin, dass die Datei stets neu angelegt werden soll – selbst wenn im Pfad bereits eine Datei mit gleichem Namen existieren sollte. Zum Abschluss übernimmt die ExecuteToStream-Methode das FileStream-Objekt als ein Argument, damit das SqlXmlCommand-Objekt Programmieren von XML mit Visual Basic .NET 463 677.book Page 464 Thursday, December 5, 2002 2:26 PM weiß, wo das von diesem Objekt generierte XML-Dokument abgelegt werden soll. Die Prozedur wird dann mit dem Schließen des FileStream-Objekts beendet. Dieser Schritt übergibt die Kontrolle von der Datei zurück an die Anwendung. Sub SaveDBQueryAsXmlToFile() ' Angeben einer Verbindungszeichenfolge für SqlXmlCommand. Dim cnn1String As String = _ "Provider=SQLOLEDB;Server=(local);" & _ "database=Northwind;" & _ "Integrated Security=SSPI" ' Angeben der Verbindung für das SqlXmlCommand-Objekt cmd1. Dim cmd1 As SqlXmlCommand = _ New Microsoft.Data.SqlXml.SqlXmlCommand(cnn1String) ' Bezeichnen der Datenquelle für cmd1. cmd1.RootTag = "Shippers" cmd1.CommandType = SqlXmlCommandType.Sql cmd1.CommandText = "SELECT * FROM Shippers " & _ "FOR XML AUTO" ' Angeben des Pfades und der Datei für das XML-Resultset. ' Danach Instantiieren eines Stream-Objekts für ' die Dateiinhalte. Dim myXMLfile As String = _ "c:\SQL Server Development with VBDotNet\" & _ "Chapter12\myShippersFromFORXML.xml" Dim myFileStream As New System.IO.FileStream _ (myXMLfile, System.IO.FileMode.Create) ' Ausführen von cmd1 und ' Speichern des Resultsets im Stream-Objekt. cmd1.ExecuteToStream(myFileStream) ' Schließen des FileStream-Objekts, um die ' Ressource freizugeben. myFileStream.Close() End Sub Die Abbildung 12.1 zeigt ein XML-Dokument, das von der Prozedur SaveDBQueryAsXmlToFile generiert wurde. Ich bin in das Verzeichnis der Datei anhand des an myXMLfile übergebenen Pfades gewechselt und habe die Datei im Windows-Explorer geöffnet. Das Stammelement ist Shippers, entsprechend dem Wert, der der RootTag-Eigenschaft des SqlXmlCommand-Objekts zugewiesen wurde. Die Zeilenwerte aus der Shippers-Tabelle erscheinen als Attribute in dem jeweils einer Zeile entsprechenden Element. Auf Grund der Art und Weise, wie die FOR XML-Klausel das Resultset formatiert, verfügt jede Zeile über den gleichen Elementnamen. Der Name des Elements entspricht dem Namen der Zeilenquelle für die SELECT-Anweisung – namentlich Shippers für die Shippers-Tabelle. HINWEIS: Es ist zwar eine gute Praxis, den Namen des Stammelementes von den anderen Elementen im XML-Dokument verschieden zu benennen, allerdings beweisen die SaveDBQueryAsXmlToFile-Prozedur und deren in Abbildung 12.1 dargestellte Ausgabe, dass Sie mit einem SqlXmlCommand-Objekt auch XML-Dokumente generieren können, deren Stammelement den gleichen Namen wie die anderen Elemente besitzt. 464 Kapitel 12 677.book Page 465 Thursday, December 5, 2002 2:26 PM Abbildung 12.1: Die Ausgabe der SaveDBQueryAsXmlToFile-Prozedur wurde mit einem Browser im Windows-Explorer geöffnet Erstellen von XML-Dokumenten mit einem kommentierten Schema Ein kommentiertes Schema macht es möglich, dass Sie das Format von Resultsets angegeben können. Bei Angabe einer FOR XML-Klausel in einer SELECT-Anweisung erscheinen die Spaltenwerte im Resultset stets als Attribute. Das folgende XML-Skript zeigt ein kommentiertes Schema für die Shippers-Tabelle. Das Schema trägt den Namen Shippers1.xsd und befindet sich im Stammordner des XMLSamples-Projekt. Nach der ersten XML-Dokumentdeklarierung (denken Sie daran, dass das XSD-Schema ein XML-Dokument ist), spezifiziert das Listing zwei Namespaces für die Formulierungen im Schema. Der erste Namespace verweist auf die Site des World Wide Web Consortium für die XML-Schemaspezifikation. Der zweite Namespace verweist auf einen universellen Ressourcennamen (URN) von Microsoft für die Zuordnung von kommentierten Schemas. Der Hauptbereich des Schemas beginnt mit der Deklaration eines Elements namens Shipper. Dieses Element verfügt über das Kommentarattribut sql:relation, welches dieses Element an die Datenquelle Shippers bindet. Wenn eine Anwendung mit einer Verbindung zur Northwind-Datenbank dieses Schema referenziert, kann die Anwendung Zeilen aus der Shippers-Tabelle entsprechend dem Format des Schemas abfragen. Das Schema definiert die Formatierung der ShipperID-Spaltenwerte als Attribute und CompanyName und Phone als Elemente. Der Einsatz des sequence-Elementes bewirkt, dass das CompanyName-Element für jeden Shipper-Eintrag vor dem Phone-Element stehen muss. <?xml version="1.0" encoding="utf-8" ?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:sql="urn:schemas-microsoft-com:mapping-schema"> <xs:element name="Shipper" sql:relation="Shippers"> <xs:complexType> <xs:sequence> <xs:element name="CompanyName" type="xs:string" /> <xs:element name="Phone" type="xs:string" /> </xs:sequence> <xs:attribute name="ShipperID" type="xs:int" /> Programmieren von XML mit Visual Basic .NET 465 677.book Page 466 Thursday, December 5, 2002 2:26 PM </xs:complexType> </xs:element> </xs:schema> Sie können ein kommentiertes Schema zusammen mit einem SqlXmlCommand-Objekt verwenden, um ein Resultset aus der Shippers-Tabelle der Northwind-Datenbank zurückzugeben, und Sie können das Resultset lokal ablegen, wenn Sie dieses als XML-Dokument in einer Datei speichern. Wenn Sie ein kommentiertes Schema zur Formatierung eines von einem SqlXmlCommand-Objekt zurückgegebenen Resultset verwenden sollen, müssen Sie den Ort der Schemadatei in einer der SqlXmlCommand-Eigenschaften angeben. Geben Sie den Pfad auf das Schema mit der SchemaPath-Eigenschaft an. Sie können den Pfad entweder als absolute oder als relative Adresse angeben. Wenn Sie eine relative Adresse verwenden, ist die Adresse relativ zur .exe-Datei der .NET-Anwendung, die sich im \Bin-Unterordner des Projektstammordners befindet. Wenn Sie also das Schema im Stammordner der Anwendung abspeichern, sollten Sie die SchemaPath-Eigenschaft wie folgt setzen ../Schemaname.xsd. Die Zeichen ../ geben das dem \Bin-Unterordner übergeordnete Verzeichnis an, welches der Stammordner der Anwendung ist. Wenn Sie die SchemaPath-Eigenschaft verwenden, müssen Sie die CommandText-Eigenschaft für das SqlXmlCommand-Objekt mit einem XPath-Ausdruck angeben. Wenn Sie nur eine einzelne Tabelle zurückgeben wollen und das Schema nur eine Tabelle bezeichnet, kann der XPath-Ausdruck sehr einfach aufgebaut sein. Führen Sie einfach die äußersten Elemente im Schema auf. In unserem Fall ist dies Shipper. Die RunAnnotatedSchemaXPathQuery-Prozedur zeigt die Syntax für die Generierung eines XMLDokumentes anhand des kommentierten Schemas Shippers1.xsd. Ungeachtet vieler Ähnlichkeiten mit dem vorangegangenen Beispiel unterscheiden sich diese doch in mehreren Gesichtspunkten. Die wichtigsten Unterschiede wurden in Fettschrift dargestellt. Beachten Sie, dass das SqlXmlCommand-Objekt aus diesem Beispiel über eine SchemaPath-Eigenschaft verfügt. Sie können dieser Eigenschaft einen Zeichenfolgenwert zuweisen, um auf den Ort des kommentierten Schemas hinzuweisen. Als nächstes gibt die CommandType-Eigenschaft eine XPath-Abfrage an. Die CommandText-Eigenschaft definiert die Abfrage in Form eines XPath-Ausdrucks. Neben diesen Unterschieden sind alle weiteren Änderungen kosmetischer Natur oder sind wenigstens nicht wesentlich. Die wichtigste dieser geringfügigen Änderungen ist im Pfad und Dateinamen für die Speicherung des vom SqlXmlCommand-Objekt generierten XML-Dokumentes zu finden. In diesem Beispiel lautet der Dateiname myShippersFROMANNOTATEDSCHEMA.xml. Auf Grund des vom vorangegangenen Beispiel verschiedenen Namens können Sie die Ausgabe der beiden Beispiele unmittelbar miteinander vergleichen. Sub RunAnnotatedSchemaXPathQuery() ' Angeben einer Verbindungszeichenfolge für SqlXmlCommand. Dim cnn1String As String = _ "Provider=SQLOLEDB;Server=(local);" & _ "database=Northwind;" & _ "Integrated Security=SSPI" ' Angeben der Verbindung für das SqlXmlCommand-Objekt cmd1. Dim cmd1 As SqlXmlCommand = _ New Microsoft.Data.SqlXml.SqlXmlCommand(cnn1String) ' Bezeichnen des Stammelements für die XML-Datei. cmd1.RootTag = "Shippers" ' Angeben einer XPath-Abfrage auf der Basis eines ' kommentierten Schemas, das das Shipper-Element 466 Kapitel 12 677.book Page 467 Thursday, December 5, 2002 2:26 PM ' aus dem Schema Shippers1.xsd verwendet, welches sich ' im übergeordneten Verzeichnis der ' EXE-Datei der Anwendung befindet. cmd1.SchemaPath = ("..\Shippers1.xsd") cmd1.CommandType = SqlXmlCommandType.XPath cmd1.CommandText = "Shipper" ' Benennen der Datei für das XML-Resultset, danach ' Instantiieren eines Stream-Objektes für die ' Inhalte der Datei. Dim myXMLfile As String = _ "c:\SQL Server Development with VBDotNet\" & _ "Chapter12\myShippersFROMANNOTATEDSCHEMA.xml" Dim myFileStream As New System.IO.FileStream _ (myXMLfile, System.IO.FileMode.Create) ' Ausführen von cmd1 und Speichern des Resultsets ' im Stream-Objekt bevor das Stream-Objekt geschlossen wird. cmd1.ExecuteToStream(myFileStream) myFileStream.Close() End Sub Die Abbildung 12.2 zeigt das XML-Dokument, das mit der RunAnnotatedSchemaXPathQuery-Prozedur abgespeichert wurde. Beachten Sie, dass dieses Dokument ein anderes Format aufweist als das Dokument auf der Grundlage einer SELECT-Anweisung mit einer FOR XML-Klausel. Tatsächlich hat das Dokument aus Abbildung 12.2 das Format, welches vom vorigen kommentierten Schema definiert wurde. Sie können das Schema anpassen, um nur eine Teilmenge von Spaltenwerten zu erfassen, oder die Elemente anders anordnen oder die Spaltenwerte austauschen, die als Elemente und Attribute dargestellt werden. Der Einsatz einer XPath-Abfrage mit einem kommentierten Schema versetzt Sie also in die Lage, das Format des XML-Dokumentes angeben zu können. Abbildung 12.2: Die Ausgabe der RunAnnotatedSchemaXPathQuery-Prozedur wurde im Windows-Explorer mit einem Browser geöffnet Programmieren von XML mit Visual Basic .NET 467 677.book Page 468 Thursday, December 5, 2002 2:26 PM Erstellen kommentierter Schemata Bis jetzt haben Sie erfahren, wie flexibel kommentierte Schemata sind. Sie könnten sich nun fragen, ob es eine einfache Möglichkeit gibt, um diese mit Visual Studio .NET zu erstellen. Als Entwickler haben Sie die Wahl zwischen wenigstens ein paar Techniken für die Erstellung kommentierter Schemata und deren Implementierung in Ihre Anwendungen. Erstellen eines Schemas mit Code Nehmen wir an, dass Ihnen bereits ein Schema zur Verfügung steht, z. B. Shippers1.xsd, das mit einem anderen Texteditor entwickelt wurde. Es wäre sinnvoll, den Text des kommentierten Schemas nach Visual Studio .NET zu kopieren, um das Schema zu optimieren und im Verzeichnis der Anwendung abzuspeichern. Visual Studio .NET bietet zu diesem Zweck einen XML-Designer. Wählen Sie aus dem Menü Projekt von Visual Studio .NET den Befehl Neues Element hinzufügen und wählen Sie aus dem Dialogfeld Neues Element hinzufügen die Vorlage XML-Schema, um ein neues Fenster im XML-Designer zu öffnen. Akzeptieren Sie den vorgeschlagenen Namen oder geben Sie einen neuen Namen ein, z. B. Shippers2. Der Designer wird automatisch die .xsd-Dateinamenserweiterung hinzufügen und die Schemadatei im Stammordner der aktuellen Anwendung abspeichern. Falls gewünscht, können Sie dieses Verhalten außer Kraft setzen. HINWEIS: Die Speicherung von kommentierten Schemadateien und anderen Ressourcendateien im Ordner der Anwendung erleichtert die Bereitstellung. Die Bereitstellung wird erleichtert, da sich alle Dateien in einem gemeinsamen Ordner befinden, den Sie auf andere Computer kopieren können. Der XML-Designer unterstützt die Anzeige eines Schemas in zwei Ansichten – einer grafischen in der Schema-Ansicht und einer textbasierten in der XML-Ansicht. Sie können die gewünschte Ansicht über die Registerkarten im unteren Bereich des Fensters wählen. Um ein neues Schema von Grund auf zu erstellen, sollten Sie zur XML-Ansicht wechseln. Die Vorlage fügt automatisch eine Deklaration des Dokumenttyps hinzu sowie verschiedene andere Namespace- und diesbezügliche Einstellungen. Sie können weitere Einstellungen hinzufügen oder vorhandene entsprechend Ihren Erfordernissen anpassen. Um ein Beispiel zu geben, Sie können die Deklaration eines benutzerdefinierten Namespaces hinzufügen, um spezielle Attribute und Elementnamen zu definieren. Beginnen Sie mit der Eingabe des neuen Schemas nach der letzten Namespacedeklaration und vor dem abschließenden Schematag (</xs:schema>). Wenn Sie den Text eines bereits existierenden Schemas in den Designer kopieren wollen, sollten Sie den Text in die Zwischenablage von Windows kopieren und dann zu Visual Studio .NET wechseln. Zeigen Sie die XML-Ansicht eines neuen Designerfensters an. In Abhängigkeit von der Quelle des Originalschemas werden Sie das kopierte Schema editieren müssen. Eckige Klammern (< und >) könnten beispielsweise als XML-Steuerzeichen &lt; und &gt; dargestellt werden. Dieses Erfordernis ist mir besonders beim Kopieren von Beispielen aus der Visual Studio .NET-Dokumentation aufgefallen. Sie können die Option Ersetzen aus dem Menü Bearbeiten unter Suchen und Ersetzen in Visual Studio .NET wählen, um alle Steuerzeichen in < und > zu ändern. Die Abbildung 12.3 zeigt die XMLAnsicht eines kopierten Schemas bei der Bearbeitung. 468 Kapitel 12 677.book Page 469 Thursday, December 5, 2002 2:26 PM Abbildung 12.3: Die XML-Ansicht eines kopierten Schemas kurz vor dem Ersetzen des Steuerzeichens &lt; mit < Grafische Erstellung eines Schemas Einige Leser könnten die Tatsache begrüßen, dass Visual Studio .NET kommentierte Schemas mit grafischen Techniken erstellen kann. Wieder werden Sie mit einer neuen Vorlage beginnen. Der Name des ersten Schemas, das Sie in einem Projekt erstellen, wird XMLSchema1.xsd lauten, es sei denn, Sie definieren einen benutzerdefinierten Namen. Der Zähler wird für jedes nachfolgende Schema, das Sie mit der Standardnamenskonvention zum Projekt hinzufügen, um eins erhöht. Öffnen Sie im neuen Fenster des XML-Designers die Schema-Ansicht und wählen Sie dann aus dem Menü Ansicht den Befehl Server-Explorer. Expandieren Sie im Fenster Server-Explorer den Knoten Server und den Server, den Sie verwenden wollen, expandieren Sie dann SQL Server und die gewünschte SQL Server-Instanz, und wählen Sie dann die Datenbank auf dieser Serverinstanz aus. Expandieren Sie Tabellen, um die Tables-Ansammlung aus der Datenbank als Quelle für das Schema anzugeben. Ziehen Sie dann eine oder mehrere Tabellen aus dem Fenster Server-Explorer auf die Schema-Ansicht Ihres neuen Schemas, XMLSchema1.xsd. Die Abbildung 12.4 zeigt das Schema XMLSchema1.xsd nachdem die Shippers-Tabelle aus der Northwind-Datenbank einer SQL ServerInstanz namens CCS1 angegeben wurde. Die grafische Ansicht zeigt die Definition eines ShippersEintrags in einem Dokument. Die Definition des Primärschlüssels ist erkennbar – beachten Sie das Symbol eines Schlüssels neben dem ShipperID-Element in der Definition der Entität Shippers. Dieses grafisch erstellte Schema kommt Ihren Erfordernissen zwar sehr nahe, erfordert allerdings eine geringfügige Optimierung für die Verwendung mit einem SqlXmlCommand-Objekt. Sie können diese Anpassung in der XML-Ansicht vornehmen. Das folgende Listing entspricht dem Schema aus Abbildung 12.4 in der XML-Ansicht. Die Zeilen der Elemente ShipperID, CompanyName und Phone werden jeweils mit einem Zeilenumbruch dargestellt. Im Designer erscheint jedes dieser Elemente als eine lange Zeile. Die in fett dargestellten Zeilen müssen entfernt werden. Diese zu entfernenden Zeilen werden in der XML-Ansicht des Designers nicht in Fettschrift dargestellt. Wenn Sie die Zeilen mit fett dargestelltem Text löschen und das Schema als XMLSchema1.xsd abspeichern, erstellen Sie ein Schema, das Sie wie das manuell erstellte Schema Schippers1.xsd verwenden können. Die Anweisungen zum Editieren des Schemas führen zur Entfernung des Document-Feldes und der Primärschlüsselangabe für ShipperID aus der Schema-Ansicht von XMLSchema1.xsd. Programmieren von XML mit Visual Basic .NET 469 677.book Page 470 Thursday, December 5, 2002 2:26 PM Abbildung 12.4: Die ursprüngliche grafische Ansicht der Shippers-Tabelle, die von der NorthwindDatenbank auf das Schema XMLSchema1.xsd gezogen wurde. HINWEIS: Das grafisch generierte Schema verwendet keine speziellen SQLXML-XSD-Kommentarattribute, wie sql:relation, denn grafisch generierte kommentierte Schemata können über die Verbindung des SqlXmlCommand-Objektes eine Verbindung herstellen und die Namen der Elemente und Attribute mit denen der Quelldatenbankobjekte synchronisieren. Um ein Beispiel zu geben, der Elementname Shippers entspricht der Shippers-Tabelle aus der Northwind-Datenbank. Auf ähnliche Weise entsprechen die Namen ShipperID, CompanyName und Phone den Spaltennamen aus der Shippers-Tabelle. <?xml version="1.0" encoding="utf-8" ?> <xs:schema id="XMLSchema1" targetNamespace="http://tempuri.org/XMLSchema1.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/XMLSchema1.xsd" xmlns:mstns="http://tempuri.org/XMLSchema1.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="Document"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="Shippers"> <xs:complexType> <xs:sequence> <xs:element name="ShipperID" msdata:ReadOnly="true"_ 470 Kapitel 12 677.book Page 471 Thursday, December 5, 2002 2:26 PM msdata:AutoIncrement="true" type="xs:int" /> <xs:element name="CompanyName" type="xs:string" /> <xs:element name="Phone"_ type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="DocumentKey1" msdata:PrimaryKey="true"> <xs:selector xpath=".//mstns:Shippers" /> <xs:field xpath="mstns:ShipperID" /> </xs:unique> </xs:element> </xs:schema> Für die Generierung von XML-Dokumenten auf der Grundlage der Inhalte aus der Shippers-Tabelle der Northwind-Datenbank habe ich eine Prozedur namens RunNETGeneratedSchemaXPathQuery erstellt. Diese Prozedur ist mit dem Schema RunAnnotatedSchemaXPathQuery, das weiter oben angegeben wurde, mit Ausnahme zweier Zeilen identisch. Die beiden zu ersetzenden Zeilen werden an dieser Stelle angegeben. Beachten Sie, dass das neue Beispiel ein anderes Schema verwendet, als das vorige Beispiel. Darüber hinaus wurde die CommandText-Eigenschaft von Shipper zu Shippers geändert. Dies ist erforderlich, denn das Schema XMLSchema1.xsd verwendet Shippers, um den Elementnamen für Shipper-Einträge anzugeben, wogegen Shipper1.xsd den Namen Shipper verwendete. Würden Sie die CommandText-Eigenschaft des SqlXmlCommand-Objekts nicht anpassen, würde die XPath-Abfrage aus dem Beispiel fehlschlagen. cmd1.SchemaPath = ("..\XMLSchema1.xsd") cmd1.CommandText = "Shippers" Das komplette Listing der RunNETGeneratedSchemaXPathQuery-Prozedur ist in den Beispieldateien zu diesem Buch zu finden. Dynamische Angabe von XML-Resultsets Der vorangegangene Abschnitt konzentrierte sich auf die Erstellung von XML-Resultsets auf der Grundlage einer festen Quelle, z. B. allen Zeilen und allen Spalten aus der Shippers-Tabelle. Diese allgemeine Strategie hat den Vorteil, dass die Ressourcen aus der Remotedatenbank lokal gespeichert werden. Die Beispiele aus diesem Abschnitt erweitern die Funktionalität der Beispiele aus dem vorangegangenen Abschnitt, um Datenquellen zu unterstützen, die dynamisch zur Laufzeit definiert werden. Sie können lokale XML-Dokumente abfragen, die Sie entweder mit der aktuellen Prozedur temporär oder zuvor mit einer anderen Prozedur erstellt haben. Das erste Beispiel aus diesem Abschnitt erstellt ein spezifisches XML-Dokument und speichert dieses lokal ab, bevor das Dokument mit einem bestimmten XPath-Ausdruck abgefragt wird. Das zweite Bespiel aus diesem Abschnitt verdeutlicht die temporäre Erstellung von XML-Dokumenten und deren Abfrage auf beliebige Art und Weise. Der Einsatz eines XML-Dokumentes als Quelle Ihrer Abfragen eliminiert die Abhängigkeit von einer Verbindung zum Datenbankserver. Es gibt allerdings Situationen, in denen Ihre Anwendung auf die aktuellsten Daten zugreifen muss (in denen deshalb ein lokales XML-Dokument ungeeignet ist). Die Programmieren von XML mit Visual Basic .NET 471 677.book Page 472 Thursday, December 5, 2002 2:26 PM abschließenden Beispiele aus diesem Abschnitt demonstrieren zwei fortschreitend flexiblere Strategien für die Abfrage einer Remotedatenbank von SQL Server und die Anzeige der Ergebnisse mit XML. Ausführen einer XPath-Abfrage für ein spezifisches XML-Dokument Sie können ein Resultset nicht nur in einem XML-Dokument speichern, Sie können das Dokument auch direkt verwenden und abfragen. Wenn Sie mit einem XML-Dokument arbeiten, können Sie dieses jedoch nicht mit einer SQL-Anweisung abfragen. In dieser Situation stellt XPath eine ideale Lösung dar, um ein Resultset abzuleiten, das bestimmten Kriterien entspricht. Das Beispiel aus diesem Abschnitt, RunXPathQueryWithArgumentForALocalDocument, fragt ein lokales Dokument ab. Die Prozedur erstellt ein Dokument anhand der Products-Tabelle aus der Northwind-Datenbank. Danach extrahiert die Prozedur die Knoten, deren Discontinued-Wert gleich 1 ist. Dieser Wert signalisiert, dass das entsprechende Produkt nicht mehr länger für den Verkauf verfügbar ist. Die Prozedur beginnt mit der Verwendung einer SELECT-Anweisung mit FOR XML-Klausel, um alle Zeilen aus der Products-Tabelle der Northwind-Datenbank zu extrahieren. Statt die ExecuteToStream-Methode, wie im Beispiel aus dem vorangegangenen Abschnitt demonstriert, zu verwenden, wird in diesem Beispiel die ExecuteXmlReader-Methode des SqlXmlCommand-Objekts aufgerufen. Wie bereits erwähnt liefert diese Methode ein XMLReader-Objekt (xrd1) zurück, welches den schnellen, vorwärtsgerichteten, nicht zwischengespeicherten Zugriff auf das Stream-Objekt ermöglicht, das die XML-Daten enthält. Statt der Speicherung des XML-Dokumentes in einem FileStreamObjekt, wie in den Beispielen aus dem vorangegangenen Abschnitt, stellt dieses Beispiel die XMLDaten über ein XMLReader-Objekt zur Verfügung. Der nächste Schritt ist die Erstellung eines XMLDokumentes auf der Grundlage der Inhalte des XMLReader-Objekts. Die Prozedur erledigt dies in zwei Schritten. Erstens, es wird ein XMLDocument-Objekt deklariert, xdc1. Zweitens, die Prozedur ruft die Load-Methode für das Dokument mit dem XMLReader-Objekt als Argument auf. Unmittelbar nach dem Füllen des XML-Dokumentes mit dem XMLReader-Objekt schließt die Prozedur das XMLReader-Objekt, um dessen Ressourcen so schnell wie möglich wieder freizugeben. Das Laden des XML-Dokumentes mit dem XMLReader-Objekt stellt die XML-Daten aus der SELECT-Anweisung im Arbeitsspeicher bereit. Wäre die Aktualität der Daten kein Problem (weil die Daten sich nur selten oder nie ändern) könnten Sie das xdc1-Objekt mit einer zuvor abgespeicherten Kopie des XML-Dokumentes laden. (Ein nachfolgendes Beispiel demonstriert die Syntax für diese Vorgehensweise.) Das Beispiel aus dem vorangegangenen Abschnitt demonstrierte, wie XML-Daten von einem Remoteserver lokal abgelegt werden können. Mithilfe eines XPath-Ausdrucks generiert die Prozedur ein XmlNodeList-Objekt (xnl1), welches, wie bereits erwähnt, eine Ansammlung von Knoten aus einem Dokument darstellt. Die Ansammlung entspricht in unserem Fall dem XPath-Ausdruck aus der RunXPathQueryWithArgumentForALocalDocument-Prozedur. Ein Knoten ist ein XPath-Objekt aus einem Dokument. Dieses Objekt kann Elemente, Attribute und andere Features von XML-Dokumenten umfassen. Denken Sie daran, dass das XML-Dokument in diesem Fall das Resultset einer SELECT-Anweisung mit einer FOR XMLKlausel aus der Prozedur darstellt. Die XPath-Anweisung liefert alle Elemente zurück, deren Discontinued-Attribut einem Wert von 1 entspricht. Der XPath-Ausdruck wird auf alle Knoten im XML-Dokument angewendet, denn die Konten werden anhand der DocumentElement-Eigenschaft ausgewählt, welche das Stammelement des XML-Dokumentes beinhaltet. Nach der Erstellung des XmlNodeList-Objekts greift die Prozedur auf die Liste der Knoten zweimal zu. Erstens, es wird die Anzahl der Knoten in xnl1 bestimmt. Zweitens, die Prozedur gibt die XML-Daten der einzelnen Knoten in xnl1 aus. 472 Kapitel 12 677.book Page 473 Thursday, December 5, 2002 2:26 PM Sub RunXPathQueryWithArgumentForALocalDocument() ' Angeben einer Verbindungszeichenfolge für SqlXmlCommand. Dim cnn1String As String = _ "Provider=SQLOLEDB;Server=(local);" & _ "database=Northwind;" & _ "Integrated Security=SSPI" ' Angeben einer Verbindung für das SqlXmlCommand-Objekt cmd1. Dim cmd1 As SqlXmlCommand = _ New Microsoft.Data.SqlXml.SqlXmlCommand(cnn1String) ' Bezeichnen der Datenquelle für cmd1 mit ' einem Resultset in XML-Format. cmd1.RootTag = "Products" cmd1.CommandType = SqlXmlCommandType.Sql cmd1.CommandText = "SELECT * FROM Products FOR XML AUTO" ' Übergeben des Resultsets von cmd an einen XmlReader und ' Laden eines XmlDocument-Objekts mit den ' Inhalten des XmlReader–Objekts. Dim xrd1 As System.Xml.XmlReader = cmd1.ExecuteXmlReader() Dim xdc1 As New System.Xml.XmlDocument() xdc1.Load(xrd1) ' Schließen des XmlReader–Objekts. xrd1.Close() ' Angeben einer XPath-Abfrage für die Knoten ' aus dem XmlDocument xdc1 mit einem ' Discontinued-Wert von 1. Dim xnl1 As System.Xml.XmlNodeList = _ xdc1.DocumentElement.SelectNodes _ ("//Products[@Discontinued=1]") ' Deklarieren eines Knotens und einer Zeichenfolge. Dim xnd1 As System.Xml.XmlNode Dim str1 As String ' Anzeigen einer Meldung für jeden Knoten, um die Inhalte, ' einschließlich der XML-Tags anzuzeigen. Debug.WriteLine( _ "Die Anzahl von Zeilen in diesem Resultset ist " & _ xnl1.Count.ToString & ".") For Each xnd1 In xnl1 str1 = xnd1.OuterXml Debug.WriteLine(str1) Next End Sub Die Abbildung 12.5 zeigt einen Ausschnitt aus dem Ausgabefenster mit den von der RunXPathQueryWithArgumentForALocalDocument-Prozedur generierten Ergebnissen. Die Abbildung zeigt acht Produkte aus dem anhand der Products-Tabelle erstellten XML-Dokument an, deren DiscontinuedWert 1 ist. Die ProductID-Spaltenwerte der nicht mehr verfügbaren Produkte lauten 5, 9, 17, 24, 28, 29, 42 und 53. Beachten Sie, dass die ProductID-Spaltenwerte als XML-Attributswerte dargestellt Programmieren von XML mit Visual Basic .NET 473 677.book Page 474 Thursday, December 5, 2002 2:26 PM werden. Dies entspricht der Tatsache, dass die Prozedur die OuterXML-Anweisung aufruft, um die tatsächlichen XML-Daten für jeden einzelnen Knoten von xnl1 auszugeben. Abbildung 12.5: Die RunXPathQueryWithArgumentForALocalDocument-Prozedur liefert XML-Informationen zu nicht mehr länger verfügbaren Produkten zurück Ausführen einer XPath-Abfrage für beliebige XML-Dokumente Das vorangegangene Beispiel ist interessant, denn es demonstriert eine praktische Einsatzmöglichkeit für XML-Dokumente auf der Grundlage von Datenbankobjekten. Insbesondere können Sie Datenbankinhalte über eine lokale Kopie aus einem XML-Dokument verarbeiten. Allerdings funktioniert das Beispiel aus dem vorangegangenen Abschnitt mit nur einem XML-Dokument. Damit das Beispiel auch mit einem anderen XML-Dokument eingesetzt werden kann, müssen Sie die interne Struktur der Prozedur analysieren und bestimmte Codezeilen ändern. Dies ist schwierig. Es wäre viel besser, wenn Sie Parameter übergeben könnten, um das XML-Dokument und die Abfrage zu definieren, damit die Prozedur ein passendes Resultset generieren kann. Das Beispiel aus diesem Abschnitt demonstriert, wie eine derartige Lösung programmiert werden kann. Ich habe zwei Codeabschnitte entwickelt, um die Beispielprozedur aus diesem Abschnitt aufzurufen. Der erste Codeabschnitt führt die gleiche XPath-Abfrage für das gleiche XML-Dokument aus dem vorangegangenen Beispiel aus. Der Unterschied besteht darin, dass dieses Beispiel die SQL-Informationen für die Definition des Dokumentes und die Definition der XPath-Abfrage für das Dokument als Argumente übergibt. Es ist nicht überraschend, dass der Aufruf der Prozedur in diesem Abschnitt das gleiche Resultset wie das Beispiel aus dem vorangegangenen Abschnitt generiert. Der zweite Codeblock verwendet die gleiche Prozedur, um eine andere XPath-Abfrage für ein anderes XML-Dokument auszuführen. Es ist zwar nicht verwunderlich, dass wir unterschiedliche Ergebnisse mit der gleichen Prozedur erhalten, das Beispiel ist aber dennoch interessant, denn es demonstriert, wie einfach es ist, dieses Verhalten mit einer XPath-Abfrage und einem XML-Dokument zu erreichen – beide sind typischen Visual Basic-Entwicklern unbekannt. Der erste Codeblock besteht aus drei Codezeilen. Die erste Zeile weist der Zeichenfolgenvariablen strSQL einen Wert zu. Diese Speichervariable enthält die SQL-Zeichenfolge für das Resultset, welche von der RunXPathQueryWithArgumentForAnyLocalDocument-Prozedur verwendet wird, um ein XML-Dokument zu füllen. Die zweite Codezeile weist der Zeichenfolgenvariablen strXPath einen Wert zu. Diese Variable speichert die XPath-Abfrage für das von der Prozedur generierte XML-Dokument. Durch die Anwendung dieser XPath-Abfrage auf das XML-Dokument wird das Resultset generiert. Ein mögliches Resultset ist in Abbildung 12.5 dargestellt. Die letzte Codezeile aus dem ersten Codeblock übergibt die Variablen strSQL und strXPath an die RunXPathQueryWithArgumentForAnyLocalDocument-Prozedur. Die Prozedur führt wiederum die SQL-Abfrage aus und füllt das 474 Kapitel 12 677.book Page 475 Thursday, December 5, 2002 2:26 PM XML-Dokument mit dem Resultset. Zum Abschluss listet die Prozedur die Knoten im Ausgabefenster auf. Dim strSQL As String = "SELECT * FROM Products FOR XML AUTO" Dim strXPath As String = "//Products[@Discontinued=1]" RunXPathQueryWithArgumentForAnyLocalDocument(strSQL, strXPath) Der zweite Codeblock für den Aufruf der RunXPathQueryWithArgumentForAnyLocalDocumentProzedur wird als nächstes dargestellt. Da dieser Abschnitt die gleichen Variablen verwendet, wie der vorangegangene, sollten Sie wenigstens einen dieser Blöcke auskommentieren, um einen Kompilierungsfehler auf Grund einer doppelten Deklaration der Variablen zu vermeiden. Bei diesem Kommentar ging ich davon aus, dass beide Codeabschnitte in der gleichen Prozedur existieren, wie das in der Prozedur main von Module1 aus dem XMLSamples-Projekt der Fall ist. Der zweite Codeblock definiert über die SQL-Zeichenfolge ein anderes XML-Dokument als der erste Codeblock. Das XMLDokument aus dem ersten Codeabschnitt beinhaltete eine Liste von Produkten. Das XML-Dokument des zweiten Codeabschnitts enthält hingegen eine Liste von Angestellten. Darüber hinaus ändert sich die XPath-Abfrage, um eine bestimmte Teilmenge von Angestellten zu ermitteln. Der wichtigste Punkt des zweiten Codeblocks ist, dass Sie eine beliebige SQL-Zeichenfolge verwenden können, um ein XML-Dokument zu erstellen, und dann eine passende XPath-Abfrage, um dieses abzufragen. Sie erreichen diese Flexibilität ohne den internen Code der RunXPathQueryWithArgumentForAnyLocalDocument-Prozedur anpassen zu müssen. Sie können diese Anwendung unmittelbar erweitern, wenn Sie eine Liste von vorformulierten SQL-Abfrageanweisungen mit dazugehörigen XPath-Abfrageanweisungen bereitstellen. Auf diese Weise können Sie die Aufgabe der Generierung und Verwendung von XML-Dokumenten erheblich vereinfachen. Dim strSQL As String = "SELECT * FROM Employees FOR XML AUTO" Dim strXPath As String = "//Employees[@EmployeeID>4]" RunXPathQueryWithArgumentForAnyLocalDocument(strSQL, strXPath) Ungeachtet der wesentlich erweiterten Anwendungsflexibilität ist die Prozedur aus diesem Abschnitt annähernd mit derjenigen aus dem vorangegangenen Abschnitt identisch. Der Hauptunterschied besteht darin, dass zwei Zeichenfolgenvariablen – strSQL und strXPath – übergeben werden. Darüber hinaus ändert die Prozedur die Zuweisung der RootTag-Eigenschaft, damit diese nicht an eine Liste von Produkten sondern an eine Liste von beliebigen Entitäten gebunden wird. Die Anwendung verwendet stets eine Verbindung zur Northwind-Datenbank. Sie können die Verbindungszeichenfolge allerdings ebenfalls parametrisieren, um noch größere Anwendungsflexibilität zu erzielen. Zu guter Letzt sollten Sie die Verbindungszeichenfolge ändern, damit diese auf eine passende Datenbank ihrer Anwendung verweist. Sub RunXPathQueryWithArgumentForAnyLocalDocument( _ ByVal strSQL As String, ByVal strXPath As String) ' Angeben einer Verbindungszeichenfolge für SqlXmlCommand. Dim cnn1String As String = _ "Provider=SQLOLEDB;Server=(local);" & _ "database=Northwind;" & _ "Integrated Security=SSPI" ' Angeben einer Verbindung für das SqlXmlCommand-Objekt cmd1. Dim cmd1 As SqlXmlCommand = _ New Microsoft.Data.SqlXml.SqlXmlCommand(cnn1String) Programmieren von XML mit Visual Basic .NET 475 677.book Page 476 Thursday, December 5, 2002 2:26 PM ' Bezeichnen der Datenquelle für cmd1 mit ' einem Resultset in XML-Format. cmd1.RootTag = "MyRoot" cmd1.CommandType = SqlXmlCommandType.Sql cmd1.CommandText = strSQL ' Übergeben des Resultsets von cmd an einen XmlReader und ' Laden eines XmlDocument-Objekts mit den ' Inhalten des XmlReader–Objekts. Dim xrd1 As System.Xml.XmlReader = cmd1.ExecuteXmlReader() Dim xdc1 As New System.Xml.XmlDocument() xdc1.Load(xrd1) ' Schließen des XmlReader–Objekts. xrd1.Close() ' Angeben einer XPath-Abfrage für die Knoten ' aus dem XmlDocument xdc1. Dim xnl1 As System.Xml.XmlNodeList = _ xdc1.DocumentElement. _ SelectNodes(strXPath) ' Deklarieren eines Knotens und einer Zeichenfolge. Dim xnd1 As System.Xml.XmlNode Dim str1 As String ' Anzeigen einer Meldung für jeden Knoten, um die Inhalte, ' einschließlich der XML-Tags anzuzeigen. Debug.WriteLine( _ "Die Anzahl von Zeilen im Resultset ist " & _ xnl1.Count.ToString & ".") For Each xnd1 In xnl1 str1 = xnd1.OuterXml Debug.WriteLine(str1) Next End Sub Ausführen parametrisierter SQL Server-Abfragen Unter Umständen werden Ihre Anwendungen nicht in der Lage sein, XML-Dokumente als Datenquelle zu verarbeiten, da auf die aktuellsten Daten zugegriffen werden muss. In diesem Fall können Sie eine parametrisierte SQL Server-Abfrage verwenden. Der Parameter aus der Abfrageanweisung gibt Ihren Benutzern die Möglichkeit, ein Resultset für eine bestimmte Abfrage zur Laufzeit angeben zu können. Die SQLXML-verwalteten Klassen aus den Web-Releases 2 und 3 ermöglichen diese Art von Abfragen. Das Beispiel aus diesem Abschnitt demonstriert den Einsatz der Klasse SqlXmlParameter. Die Verwendung von Parametern in SQL-Anweisungen ist beispielsweise für Prototyptests von SQL-Code für gespeicherte Prozeduren nützlich, oder in Fällen, in denen Ihnen keine gespeicherte Prozedur mit den Parametern zur Verfügung steht, die Sie benötigen, um eine Aufgabe auszuführen. Das Beispiel aus diesem Abschnitt hat darüber hinaus den Zweck, Sie an die Techniken zur Angabe von Parametern in SQL-Anweisungen zu erinnern, und zu verdeutlichen, wie StreamReader-Objekte verwendet werden, um das Resultset einer Abfrage zu erfassen. Das Beispiel RunSQLParameterQuery beginnt mit der Angabe einer Verbindungszeichenfolge und der Instantiierung eines SqlXml- 476 Kapitel 12 677.book Page 477 Thursday, December 5, 2002 2:26 PM Command-Objektes auf der Grundlage dieser Zeichenfolge. Als nächstes setzt das Beispiel ausgewählte SqlXmlCommand-Eigenschaften, um die auszuführende Abfrage zu definieren. Beispielsweise wird die CommandText-Eigenschaft auf eine SQL-Zeichenfolge gesetzt, die einen Parameter mit einem Fragezeichen (?) angibt. Die Benutzer können das SqlXmlCommand-Objekt ausführen, um die in der Liste der SELECT-Anweisung angegebenen Informationen zu ermitteln. Der Wert des Country-Parameters bezeichnet das Land, für das das SqlXmlCommand-Objekt Ergebnisse zurückgeben soll. Im Listing der Prozedur wurde der Wert für den Country-Parameter mit Brazil fest codiert. Die Syntax für die Parameterzuweisung erfordert die Deklaration eines SqlXmlParameterObjektes, den Aufruf der CreateParameter-Methode des SqlXmlCommand-Objektes sowie eine Zuweisungsanweisung für die Value-Eigenschaft des SqlXmlParameter-Objektes. Dieses Beispiel demonstriert außerdem noch eine weitere Möglichkeit zur Erfassung der XML-Daten, die vom SqlXmlCommand-Objekt zurückgegeben werden. Im vorliegenden Fall übergibt das Beispiel das Resultset der Abfrage mit dem SqlXmlParameter-Objekt an ein Dialogfeld zur Anzeige. Die ExecuteStream-Methode des SqlXmlParameter-Objektes erstellt ein MemoryStream-Objekt mit den XML-Daten, die von der über die CommandText-Eigenschaft angegebene Abfrage erstellt werden. Der Einsatz des MemoryStream-Objektes als Argument zur Instantiierung eines StreamReaderObjektes versetzt die Prozedur in die Lage, die vom SqlXmlParameter-Objekt generierten XMLDaten als Zeichenfolge zu erfassen. Die ReadToEnd-Methode des StreamReader-Objektes liefert eine Zeichenfolge mit allen XML-Daten zurück, die vom SqlXmlParameter-Objekt generiert wurden. Unter Verwendung der Anweisung srd1.ReadToEnd als Argument für die MsgBox-Funktion zeigt die Prozedur die vom SqlXmlParameter-Objekt generierten XML-Daten an. Sub RunSQLParameterQuery() ' Angeben einer Verbindungszeichenfolge für SqlXmlCommand. Dim cnn1String As String = _ "Provider=SQLOLEDB;Server=(local);" & _ "database=Northwind;" & _ "Integrated Security=SSPI" ' Angeben einer Verbindung für das SqlXmlCommand-Objekt cmd1. Dim cmd1 As SqlXmlCommand = _ New Microsoft.Data.SqlXml.SqlXmlCommand(cnn1String) ' Bezeichnen der Datenquelle für cmd1 mit einem Parameter. cmd1.RootTag = "Customers" cmd1.CommandType = SqlXmlCommandType.Sql cmd1.CommandText = "SELECT ContactName, " & _ "CompanyName, City " & _ "FROM Customers " & _ "WHERE Country = ?For XML Auto" ' Erstellen eines Parameters für cmd1 und ' Zuweisen eines Wertes an diesen Parameter. Dim prm1 As SqlXmlParameter prm1 = (cmd1.CreateParameter()) prm1.Value = "Brazil" ' Deklarieren und Instantiieren eines Streams im ' Arbeitsspeicher und füllen des Streams mit dem ' XML-Resultset von cmd1. Dim stm1 As New System.IO.MemoryStream() stm1 = cmd1.ExecuteStream() ' Kopieren des Resultsets aus dem Stream in einen Programmieren von XML mit Visual Basic .NET 477 677.book Page 478 Thursday, December 5, 2002 2:26 PM ' StreamReader und Anzeige der Streaminhalte in ' einem Dialogfeld. Dim srd1 As New System.IO.StreamReader(stm1) MsgBox(srd1.ReadToEnd) srd1.Close() End Sub Parametrisieren beliebiger SQL-Abfragen Genauso, wie Sie das zu verarbeitende XML-Dokument parametrisieren können, können Sie das vorangegangene Beispiel auch erweitern, um die Abfrage zu parametrisieren, statt nur eine bestimmte Abfrage zu verwenden. Der Trick zur Ausführung dieser Aufgabe ist, sowohl die Abfrage als auch die Parameterwerte an die Prozedur zu übergeben, die die Abfrage ausführen soll. Auf der anderen Seite muss die Prozedur zur Ausführung der Abfrage aus dem vorangegangenen Beispiel angepasst werden, um diese Parameter zu akzeptieren und diese zur Generierung einer Zeichenfolge für das Dialogfeld zu verwenden, welches die Ergebnisse am Ende der Prozedur anzeigt. Der folgende Codeblock zeigt den Setupcode, der erforderlich ist, bevor die Prozedur zur Ausführung der Abfrage und zur Anzeige der Ergebnisse in einem Dialogfeld aufgerufen werden kann. Beachten Sie, dass der Setupcode eine andere Abfrage angibt, als das Beispiel aus dem vorangegangenen Abschnitt. Die Abfrage aus diesem Abschnitt ist für die Shippers-Tabelle bestimmt. Die Abfrage aus dem vorangegangenen Abschnitt verwendete die Customers-Tabelle. Nichtsdestoweniger ist der Code für die Ausführung der Abfrage annähernd in beiden Abschnitten der gleiche. Wichtiger ist die Tatsache, dass Sie eine Abfrage für jede beliebige Tabelle und Kombinationen von Tabellen ausführen können, ohne Änderungen an der RunSQLParameterQueryWithPassedParams-Prozedur vornehmen zu müssen. Ihre Anwendung muss nur zwei Zuweisungen vornehmen – eine für die Zeichenfolgenvariable zur Angabe der Abfrage (strSQL) und eine andere für die Zeichenfolge zur Angabe eines Parameterwertes (strPrm1Value). Dim strSQL As String = "SELECT * FROM Shippers " & _ "WHERE ShipperID = ?For XML Auto" Dim strPrm1Value As String = "1" RunSQLParameterQueryWithPassedParams( _ strSQL, strPrm1Value) Das folgende Listing zeigt den Code der RunSQLParameterQueryWithPassedParams-Prozedur. Die Zeilen, die im Vergleich zur RunSQLParameterQuery-Prozedur aus dem vorigen Abschnitt geändert wurden, sind in Fettschrift dargestellt. Beachten Sie, dass nur vier Zeilen geändert wurden. Diese dienen hauptsächlich der Aufnahme und Verwendung der übergebenen Zeichenfolgenvariablen, die die Abfragesyntax und den Parameterwert angeben. Die Zuweisung der RootTag-Eigenschaft wurde ebenfalls geändert, um beliebige SQL-Abfragezeichenfolgen zu unterstützen. Neben diesen geringfügigen Änderungen braucht die vorige Prozedur nicht weiter angepasst zu werden, um diese mit beliebigen SQL-Abfragezeichenfolgen verwenden zu können. Sub RunSQLParameterQueryWithPassedParams( _ ByVal strSQL As String, _ ByVal strPrm1Value As String) ' Angeben einer Verbindungszeichenfolge für SqlXmlCommand. Dim cnn1String As String = _ "Provider=SQLOLEDB;Server=(local);" & _ "database=Northwind;" & _ 478 Kapitel 12 677.book Page 479 Thursday, December 5, 2002 2:26 PM "Integrated Security=SSPI" ' Angeben einer Verbindung für das SqlXmlCommand-Objekt cmd1. Dim cmd1 As SqlXmlCommand = _ New Microsoft.Data.SqlXml.SqlXmlCommand(cnn1String) ' Bezeichnen der Datenquelle für cmd1 mit einem Parameter. cmd1.RootTag = "MyRoot" cmd1.CommandType = SqlXmlCommandType.Sql cmd1.CommandText = strSQL ' Erstellen eines Parameters für cmd1 und ' Zuweisen eines Wertes an diesen Parameter. Dim prm1 As SqlXmlParameter prm1 = (cmd1.CreateParameter()) prm1.Value = strPrm1Value ' Deklarieren und Instantiieren eines Streams im ' Arbeitsspeicher und füllen des Streams mit dem ' XML-Resultset von cmd1. Dim stm1 As New System.IO.MemoryStream() stm1 = cmd1.ExecuteStream() ' Kopieren des Resultsets aus dem Stream in einen ' StreamReader und Anzeige der Streaminhalte in ' einem Dialogfeld. Dim srd1 As New System.IO.StreamReader(stm1) MsgBox(srd1.ReadToEnd) srd1.Close() End Sub Das Zusammenspiel von XML und DataSets XML-Dokumente und ADO.NET-Datasets arbeiten in vielen Szenarien zusammen. Ein Verständnis dieses Zusammenspiels und Kenntnisse über diese Szenarien können Ihnen bei der Abfrage und Verarbeitung von Daten auf der lokalen Clientarbeitsstation und auf einem Datenbankserver helfen. Dieser Abschnitt bietet eine Auswahl von Beispielen, die verdeutlichen, wie XML-Dokumente mit DataSets zu diesem Zweck eingesetzt werden können. Wie schon bei vielen anderen Themen in diesem Buch ist auch diese Präsentation nicht als allumfassende Abhandlung aller möglichen Gesichtspunkte zu diesem Thema gedacht. Stattdessen zielt dieser Abschnitt darauf ab, eine feste Grundlage zu vermitteln, die dazu befähigt, weiterzugehen und mehr zu lernen, in welcher Richtung auch immer Ihr Bedarf liegt. Erstellen hierarchischer XML-Dokumente Einer der wirklich wertvollen Aspekte von DataSet-Objekten in ADO.NET ist, das diese Objekte auf XML aufbauen. Dies bedeutet, dass Sie die Elemente in einem Dataset verarbeiten und dabei indirekt XML-Strukturen modifizieren können. Dieses Feature ist insbesondere dann nützlich, wenn Sie mit Zeilenquellen arbeiten, die sich aus mehreren Tabellen zusammensetzen und über Eltern/KindBeziehungen verfügen, denn dieses Feature befreit den Entwickler von der Darstellung dieser komplexen Beziehungen in einem XSD-Schema. ADO.NET und XML sind zwar relativ neue Technolo- Programmieren von XML mit Visual Basic .NET 479 677.book Page 480 Thursday, December 5, 2002 2:26 PM gien für Visual Basic-Entwickler, das Objektmodell für DataSets in ADO.NET erleichtert jedoch die Verständlichkeit für all diejenigen, die über Kenntnisse im Umgang mit Objekten verfügen. Das Kapitel 10 enthält einen allgemeinen Überblick zu den Objekten von ADO.NET. Die Abbildung 10.1 in Kapitel 10 bietet einen Überblick über das DataSet-Objektmodell und zahlreiche Codebeispiele demonstrieren in Kapitel 10 verschiedene ADO.NET-Programmierthemen, einschließlich des DataSet-Objekts und dessen hierarchisch abhängigen Objekte. Ein DataSet-Objekt und dessen dazugehöriges XML-Dokument sind zwei Seiten der gleichen Münze. Mit der WriteXml-Methode des DataSet-Objekts können Sie sowohl die Inhalte eines XMLDokuments als auch das dem Dokument zugrunde liegende Schema abspeichern. Verfügt das Dataset über Änderungen, die noch nicht in die Remotedatenbank übernommen wurden, können Sie darüber hinaus mit der WriteXml-Methode Diffgramme erstellen, die das Dataset mit sämtlichen noch nicht ausgeführten Änderungen repräsentieren. Denken Sie daran, dass Diffgramme die aktuellen sowie die vorhergehenden Werte beinhalten. Diffgramme stehen unmittelbar zur Verfügung, denn ADO.NET übermittelt die Änderungen vom Client an die SQL Server-Instanz in Form von Diffgrammen. Das Beispiel aus diesem Abschnitt demonstriert, wie Sie ein Dataset mit drei Hierarchieebenen auf der Grundlage dreier Tabellen aus der Northwind-Datenbank erstellen können. Diese Tabellen sind die Customers-, Orders- und Order Details-Tabellen. Individuelle Kunden sind die übergeordneten Elemente individueller Bestellungen und Bestellungen sind wiederum den Details zu den (oder den Einzelposten der) Bestellungen übergeordnet. Diese Art von verschachtelten Beziehungen wird von XML-Dokumenten besonders gut repräsentiert, denn das Dokument gibt die tatsächliche Verschachtelung statt eines einzelnen flachen Rowsets wieder. Das Beispiel stützt sich auf zwei Prozeduren. Die erste Prozedur SaveThreeTierDasAsXmlDocument ruft die zweite Prozedur auf, die ein Dataset generiert und dieses dann als XML-Dokument ablegt. Unter Verwendung der WriteXml-Methode vermeidet die SaveThreeTierDasAsXmlDocument-Prozedur den Einsatz von SQLXML-verwalteten Klassen. Dies bedeutet, dass die in diesem Kapitel demonstrierten Techniken relativ robust aufgebaut sind, denn diese können mit einer beliebigen Datenquelle, auf die ADO.NET zugreifen kann, eingesetzt werden. Darüber hinaus erfordern die Prozeduren, die den Einsatz von DataSet-Objekten demonstrieren, weder die Installation von WebRelease 2 noch von Web-Release 3, was bei Einsatz verwalteter Klassen erforderlich wäre. Die zweite Prozedur, CreateThreeTierDataSet, ist eine Function-Prozedur, welche ein Dataset-Objekt an die aufrufende Prozedur zurückgibt. Dieses Dataset wird von der ersten Prozedur als XML-Dokument in einer Datei abgespeichert. Die SaveThreeTierDasAsXmlDocument-Prozedur beginnt mit der Instantiierung eines DataSetObjekts und der Füllung dieses Objekts mit dem von der Function-Prozedur CreateThreeTierDataSet zurückgelieferten DataSets. Nach der Füllung des DataSets bereitet die Prozedur die Speicherung der Daten in einer Datei mit Unicode-Zeichen vor. Diese Aktion erfordert mehrere Schritte. Die Prozedur beginnt diesen Prozess mit der Zuweisung des Namens des XML-Dokuments an eine Zeichenfolgenvariable (str1). Als nächstes instantiiert die Prozedur ein FileStream-Objekt (fst1), um mit der Datei zu arbeiten, in der das XML-Dokument abgespeichert wird. Danach instantiiert die Prozedur ein XmlTextWriter-Objekt (txw1), um die XML-Daten aus dem Dataset in das FileStream-Objekt zu kopieren. Die WriteXml-Methode verwendet txw1 als eines von zwei Argumenten, um die XMLDaten aus dem Dataset in eine Datei zu kopieren. Das andere Argument, welches im vorliegenden Fall auf XmlWriteMode.WriteSchema gesetzt wurde, legt fest, wie die WriteXml-Methode die Inhalte aus dem Dataset in der Datei abspeichern soll. Das Argument XmlWriteMode.WriteSchema gibt an, dass die WriteXml-Methode mit dem Kopieren des Schemas des Dokumentes beginnen und nach dem Schema mit den Inhalten des XML-Dokumentes fortsetzen soll. Nach dem Schreiben des 480 Kapitel 12 677.book Page 481 Thursday, December 5, 2002 2:26 PM Dokuments gibt die Prozedur die Ressourcen frei und übergibt die Kontrolle an die Prozedur durch Schließen des XmlTextWriter- und des FileStream-Objekts. Die CreateThreeTierDataSet-Prozedur beginnt mit der Instantiierung eines Verbindungsobjekts. Dieses Objekt wird geöffnet, um eine Verbindung zur Northwind-Datenbank herzustellen. Die Prozedur instantiiert als nächstes ein DataSet-Objekt (das1) und verwendet das Verbindungsobjekt, um ein SqlDataAdapter-Objekt (dap1) mit der Customers-Tabelle aus der Northwind-Datenbank zu verknüpfen. Danach kopiert die Prozedur über einen Aufruf der Fill-Methode des dap1-Objektes die Zeilen aus der Customers-Tabelle in ein DataTable-Objekt von das1 namens Customers. Nachdem die Customers-Tabelle aus der Northwind-Datenbank zum Dataset das1 hinzugefügt wurde, verweist die Prozedur dap1 auf die Orders-Tabelle aus der Northwind-Datenbank. Nun wird die Orders-Tabelle zu das1 hinzugefügt. Die Prozedur wiederholt diesen Prozess ein drittes und letztes Mal, um in das1 das DataTable-Objekt OrderDetails mit den Spaltenwerten aus der Order DetailsTabelle der Northwind-Datenbank zu erstellen. Nach diesen drei Aufrufen der Fill-Methode beinhaltet das Dataset das1 drei nicht miteinander in Beziehung stehende Tabellen. Wir benötigen ein DataRelation-Objekt, um die hierarchischen Beziehungen zwischen den Tabellen anzugeben. In der Tat benötigt das1 zwei DataRelation-Objekte. Ein DataRelation-Objekt drückt die Beziehungen zwischen den DataTable-Objekten Customers und Orders aus. Ein zweites DataRelation-Objekt repräsentiert die Beziehungen zwischen den DataTable-Objekten Orders und OrderDetails. Die Prozedur erstellt das erste DataRelation-Objekt über einen Aufruf der Add-Methode der Relations-Ansammlung des DataSets das1. Das erste Argument, welches das DataRelation-Objekt benennt, wurde auf den Wert CustOrders gesetzt. Die nächsten beiden Argumente identifizieren die Spalten, die verwendet werden sollen, um die beiden Datentabellen miteinander zu verknüpfen. Durch Setzen der Nested-Eigenschaft des DataRelation-Objektes auf True veranlassen Sie die verschachtelte Darstellung der Bestellungen innerhalb der Kunden im XML-Dokument. Der Standardwert der Nested-Eigenschaft ist False. In diesem Fall würde die WriteXml-Methode zwei Sätze von Spaltenwerten ohne Verschachtelung der Werte aus der einen Tabelle mit den Werten aus der anderen Tabelle darstellen. Mit einem zweiten Aufruf der Add-Methode der Relations-Ansammlung des DataSets das1 erstellt die Prozedur die zweite Datenbeziehung, um die Eltern/Kind-Struktur zwischen den DataTable-Objekten Orders und OrderDetails anzugeben. Zum Abschluss wird die Return-Anweisung der CreateThreeTierDataSet-Prozedur aufgerufen, um das Dataset das1 an die aufrufende Prozedur zu übergeben. Sub SaveThreeTierDasAsXmlDocument() ' Deklarieren und Instantiieren des DataSet das1 ' und Füllen von das1 mit dem von der Function-Prozedur ' zurückgegebenen Dataset. Dim das1 As New DataSet() das1 = CreateThreeTierDataSet() ' Deklarieren einer Zeichenfolge für den Dateinamen, ' um ein FileStream-Objekt anhand eines XmlTextWriters mit ' den Inhalten des DataSets das1 abzuspeichern. Dim str1 As String = _ "c:\SQL Server Development with VBDotNet\" & _ "Chapter12\myCustomersSchema.xml" Dim fst1 As New System.IO.FileStream _ (str1, System.IO.FileMode.Create) Dim txw1 As New System.Xml.XmlTextWriter _ (fst1, System.Text.Encoding.Unicode) ' Schreiben der XML-Daten von das1 zusammen mit dem Schema. Programmieren von XML mit Visual Basic .NET 481 677.book Page 482 Thursday, December 5, 2002 2:26 PM das1.WriteXml(txw1, XmlWriteMode.WriteSchema) ' Schließen von TextWriter und FileStream. txw1.Close() fst1.Close() End Sub Function CreateThreeTierDataSet() ' Öffnen einer Verbindung zur Northwind-Datenbank. Dim cnn1 As SqlConnection = _ New SqlConnection( _ "Data Source=localhost;" & _ "Initial Catalog=northwind;" & _ "Integrated Security=SSPI") cnn1.Open() ' Deklarieren und Instantiieren eines DataSets (das1). Dim das1 As DataSet = New DataSet("CustomerOrders") ' Deklarieren und Instantiieren eines DataAdapter (dap1), um ' das DataTable-Objekt Customers von das1 zu füllen. Dim dap1 As SqlDataAdapter = _ New SqlDataAdapter( _ "SELECT CustomerID, CompanyName, ContactName, Phone " & _ "FROM Customers", cnn1) dap1.Fill(das1, "Customers") ' Wiederverwenden von dap1, um ' das DataTable-Objekt Orders von das1 zu füllen. dap1.SelectCommand.CommandText = _ "SELECT OrderID, OrderDate, CustomerID FROM Orders" dap1.Fill(das1, "Orders") ' Wiederverwenden von dap1, um ' das DataTable-Objekt OrderDetails von das1 zu füllen. dap1.SelectCommand.CommandText = _ "SELECT * FROM [Order Details]" dap1.Fill(das1, "OrderDetails") ' Schließen der Verbindung. cnn1.Close() ' Angeben einer Beziehung zwischen den DataTable-Objekten ' Customers und Orders mit den Orders-Elementen ' verschachtelt in den Customers-Elementen. das1.Relations.Add("CustOrders", _ das1.Tables("Customers").Columns("CustomerID"), _ das1.Tables("Orders").Columns("CustomerID")). _ Nested = True ' Angeben einer Beziehung zwischen den DataTable-Objekten ' Orders und OrderDetails mit den OrderDetails-Elementen ' verschachtelt in den Orders-Elementen. das1.Relations.Add("OrderDetail", _ 482 Kapitel 12 677.book Page 483 Thursday, December 5, 2002 2:26 PM das1.Tables("Orders").Columns("OrderID"), _ das1.Tables("OrderDetails").Columns("OrderID"), _ False).Nested = True Return das1 End Function Wenn die SaveThreeTierDasAsXmlDocument-Prozedur die WriteXml-Methode mit dem zweiten Parameter gleich XmlWriteMode.WriteSchema aufruft, schreibt die Methode eigentlich zwei statt nur einem Dokument. Das dem XML-Argument entsprechende XSD-Schema erscheint vor den eigentlichen Daten. Die .NET-Dokumentation spricht bei dieser Art von Schema von einem InlineSchema, denn es wird zusammen mit den nachfolgenden XML-Daten in der Datei abgelegt. Das Schema für das XML-Dokument von das1 ist hinreichend komplex, denn es spezifiziert die Spalten aus drei Tabellen, zwei Angaben zu Datenbeziehungen und unterstützende Elemente, wie Einschränkungen, um die DataRelation-Objekte zu aktivieren. Die Abbildung 12.6 und die Abbildung 12.7 zeigen Ausschnitte aus dem Schema in Browserfenstern. Das Schema ist zu groß, um in ein Fenster zu passen. Dieses Schema erscheint am Anfang des XML-Dokuments, das in der SaveThreeTierDasAsXmlDocument-Prozedur angegeben wurde. Der Dateiname des XML-Dokuments ist myCustomersSchema.xml aus dem Verzeichnis C:\SQL Server Development with VBDot-Net\Chapter12. Beim Testen der Anwendung auf Ihrem Computer könnte es erforderlich sein, den Zielordner für das XML-Dokument zu ändern und einen auf Ihrer Arbeitsstation existierenden Ordner anzugeben. Abbildung 12.6: Der erste Teil des Inline-Schemas für ein XML-Dokument aus der Datei myCustomersSchema.xml Programmieren von XML mit Visual Basic .NET 483 677.book Page 484 Thursday, December 5, 2002 2:26 PM Abbildung 12.7: Der zweite Teil des Inline-Schemas für ein XML-Dokument aus der Datei myCustomersSchema.xml Wie Sie aus der Größe und Komplexität des Schemas erkennen können, ist es von Wert, das Schema automatisch anlegen zu können. Die Erstellung eines DataSets mit Programmcode sollte an dieser Stelle im Buch keine Schwierigkeit mehr darstellen. Auf jeden Fall ist es sehr wahrscheinlich, dass Sie den Komfort der programmatischen Erstellung von DataSets bei der Programmierung von Anwendungen mit ADO.NET bald schätzen werden. Die Verwendung eines programmatisch erstellten DataSets als Grundlage für ein Schema könnte deshalb ein sinnvoller Ansatz sein, wenn Sie sich nicht mit dem Aufbau eines XSD-Schemas von Grund auf auskennen. In der Tat könnte die Generierung eines Schemas und die Zuordnung dieses Schemas zum Design eines DataSets eine Möglichkeit darstellen, wie Sie mit Visual Basic .NET die XSD-Syntax erlernen können, sodass Sie bald Ihre eigenen komplexen Schemata von Grund auf erstellen können. Die Abbildung 12.8 zeigt einen Ausschnitt mit dem Anfang der XML-Daten aus der Datei myCustomersSchema.xml. Die gesamte erste Bestellung (OrderID 10643) ist zu erkennen sowie der Anfang der zweiten Bestellung (OrderID 10692) des Kunden mit einem CustomerID-Wert von ALFKI. Beachten Sie, wie die Bestellungen mit den Kunden verschachtelt wurden. Außerdem wurden die Einzelposten, oder Details, zu den Bestellungen mit den Bestellungen verschachtelt. 484 Kapitel 12 677.book Page 485 Thursday, December 5, 2002 2:26 PM Abbildung 12.8: Ein Ausschnitt mit dem Anfang der XML-Daten aus der Datei myCustomersSchema.xml Abfragen von untergeordneten Daten aus einem Dataset mit XPath Das hierarchische Design des DataSets das1 aus dem vorigen Beispiel stellt eine Quelle dar, die für eine Demonstration der Abfrage von untergeordneten Daten mit der XPath-Abfragesyntax geeignet ist. Wie bereits erwähnt, verfügt das Dataset über Detailinformationen zu den Bestellungen, die den Bestellungen untergeordnet sind, die wiederum den Kunden untergeordnet sind. In Abbildung 12.8 ist der erste UnitPrice-Wert von 45,6 eine untergeordnete Information der ersten Bestellung mit einem OrderID-Wert von 10643. Diese OrderID gehört zu dem Kunden mit dem CustomerID-Wert ALFKI. Die XPath-Abfragesyntax ermöglicht Ihnen die Erstellung eines Resultsets für die Kunden auf der Basis beliebiger untergeordneter Werte, z. B. UnitPrice. Das Beispiel aus diesem Abschnitt veranschaulicht, wie derartige XPath-Abfragen aufgebaut werden. Das Beispiel vermittelt darüber hinaus, wie die Knoten in einem Resultset aufgelistet werden. XPath-Abfragen liefern zwar auch eine Ansammlung von Knoten in einem XmlNodeList-Objekt zurück, die Aufzählung enthält jedoch die einzelnen Werte ohne die zusätzlichen XML-Tags, welche die Werte in einem XML-Dokument voneinander abtrennen. Die RunXPathQueryForThreeTierXmlDocument-Prozedur, welche das Beispiel aus diesem Abschnitt implementiert, beginnt mit der Instantiierung eines neuen DataSets namens das1. Dieses Dataset wird dann mit den verschachtelten Daten gefüllt, die mit der CreateThreeTierDataSet-Funktion erstellt wurden. (Siehe den vorangegangenen Abschnitt für ein Listing dieser Function-Proze- Programmieren von XML mit Visual Basic .NET 485 677.book Page 486 Thursday, December 5, 2002 2:26 PM dur.) Da ADO.NET für jedes Dataset automatisch ein XML-Dokument erstellt, können Sie entweder das Dataset oder das zugrunde liegende XML-Dokument abfragen und identische Resultsets erhalten. Die RunXPathQueryForThreeTierXmlDocument-Prozedur vermittelt eine Strategie zur Verarbeitung des XML-Dokumentes eines DataSets. Nach der Füllung des DataSets instantiiert die Prozedur ein neues XmlDataDocument-Objekt (xdc1) auf der Grundlage des DataSets das1. Die XmlDataDocument-Klasse ist eine Erweiterung der XmlDocument-Klasse, mit der .NET-Anwendungen die XML-Daten eines DataSets in ein XML-Dokument laden können. XmlDataDocument-Objekte ermöglichen den Einsatz von W3C-Verarbeitungstechniken für XML-Dokumente in Anwendungen, z. B. XPath-Abfragen. Die Prozedur demonstriert diese Möglichkeit unter Verwendung einer XPathAbfrage, die alle Kundenknoten auswählt, deren untergeordnete Informationen einen UnitPrice-Wert von größer als 100 beinhalten. Mit dem XPath-Ausdruck wird ein XmlNodeList-Objekt (xnl1) anhand der Struktur des mit dem abgefragten XmlDataDocument-Objekt verknüpften DataSets erstellt. Die Verknüpfung zwischen dem XmlDataDocument-Objekt und dem Dataset das1 ermöglicht die Auswahl einzelner Werte jedes Knotens aus dem XmlNodeList-Objekt als Spaltenwerte eines DataRow-Objektes aus dem DataSet-Objektmodell. In der Prozedur wird die Implementierung dieses Ansatzes durch die Deklarierung des DataRow-Objektes (myRow) vorbereitet. Bevor mit einer Schleife begonnen wird, liefert die Prozedur die Anzahl von Konten aus der Knotenliste xnl1 zurück. Die Schleife verwendet eine For Each-Anweisung, um nacheinander alle Knoten aus xnl1 zu durchlaufen. Die GetRowFromElement-Methode übernimmt einzelne Werte aus dem aktuellen Knoten in das DataRow-Objekt myRow. Die Methode übernimmt die Werte ohne jegliche XML-Tags. Sobald die Werte eines Knotens als Spaltenwerte im myRow-Objekt zur Verfügung stehen, erstellt die Prozedur eine Zeichenfolge aus den ersten vier Spaltenwerten. Das Schema aus Abbildung 12.6 bestätigt, dass die Spalten den Werten für CustomerID, CompanyName, ContactName und Phone entsprechen. Die letzte Anweisung aus der Schleife gibt die vier Spaltenwerte im Ausgabefenster aus. Sub RunXPathQueryForThreeTierXmlDocument() ' Deklarieren und Instantiieren des DataSets das1 ' und Füllen des DataSets mit dem von der Function' Prozedur CreateThreeTierDataSet zurückgegebenen Dataset. Dim das1 As New DataSet() das1 = CreateThreeTierDataSet() ' Deklarieren und Instantiieren eines XmlDataDocument-Objekts ' auf der Grundlage der Inhalte von das1. Dim xdc1 As System.Xml.XmlDataDocument = _ New XmlDataDocument(das1) ' Generieren eines Resultsets mit allen Kunden, die ' Produkte mit einem UnitPrice von größer als 100 ' bestellt haben. Dim xnl1 As XmlNodeList = _ xdc1.DocumentElement.SelectNodes( _ "descendant::Customers" & _ "[Orders/OrderDetails/UnitPrice>100]") ' Deklarieren von Objekten für eine Schleife durch das Resultset. Dim myRow As DataRow Dim xnd1 As XmlNode Dim str1 As String 486 Kapitel 12 677.book Page 487 Thursday, December 5, 2002 2:26 PM ’ Durchlaufen des Resultsets und Ausgeben der Werte ' im Ausgabefenster. Debug.WriteLine("Es gibt " & _ xnl1.Count.ToString & " in diesem Resultset.") For Each xnd1 In xnl1 myRow = xdc1.GetRowFromElement(CType(xnd1, XmlElement)) str1 = myRow(0) & ", " & myRow(1) & _ ", " & myRow(2) & ", " & myRow(3) Debug.WriteLine(str1) Next End Sub Die Abbildung 12.9 stellt einen Ausschnitt aus dem Ausgabefenster dar, das die von der Prozedur RunXPathQueryForThreeTierXmlDocument generierten Werte aufführt. Die erste Zeile aus diesem Ausschnitt gibt die Anzahl der Kunden wieder, die Posten mit einem UnitPrice-Wert von größer als 100 gekauft haben. Danach listet das Fenster die einzelnen Kunden auf, die diesem Kriterium entsprechen. Für jeden Kunden zeigt die Liste die dazugehörigen Werte für CustomerID, CompanyName, ContactName und Phone an. Abbildung 12.9: Ein Ausschnitt mit den ursprünglich ausgegebenen Informationen aus der Prozedur RunXPathQueryForThreeTierXmlDocument Abfragen von untergeordneten Daten aus einem XML-Dokument mit XPath Im Beispiel aus dem vorigen Abschnitt wird ein neues Dataset über einen Aufruf der Prozedur CreateThreeTierDataSet erstellt, mit der das Dataset generiert wurde. Bei Anwendungen, deren Daten sich nur langsam oder in festen Intervallen ändern, könnten Sie die Leistungsfähigkeit unter Umständen verbessern, wenn Sie eine zuvor abgespeicherte Kopie des XML-Dokumentes des DataSets verwenden. Der Einsatz einer zuvor abgespeicherten Kopie eines XML-Dokumentes kann die Last auf dem Datenbankserver verringern und die Reaktionszeit der Anwendung verbessern. Die bereits erwähnte Prozedur SaveThreeTierDasAsXmlDocument speichert XML-Dokumente auf der Grundlage der gleichen verschachtelten Datenstruktur ab, die von der CreateThreeTierDataSet-Prozedur generiert wurde. Die das XML-Dokument beinhaltende Datei heißt myCustomersSchema.xml und der Pfad auf die Datei lautet C:\SQL Server Development with VBDotNet\Chapter12. Wenn Sie den Dateinamen des Dokuments oder dessen Pfad zwecks Tests auf Ihrem System angepasst haben, werden Sie diese Informationen im Beispiel zu diesem Abschnitt ebenfalls ändern müssen. Das Beispiel aus diesem Abschnitt stützt sich auf zwei Prozeduren. Die erste Prozedur RunXPathQueryForSavedThreeTierXmlDocument verarbeitet das in myCustomersSchema.xml Programmieren von XML mit Visual Basic .NET 487 677.book Page 488 Thursday, December 5, 2002 2:26 PM abgespeicherte XML-Dokument. Die zweite Prozedur MyTagValue extrahiert die Tagwerte aus einer Zeichenfolge. Die Zeichenfolge enthält Werte, die durch XML-Tags voneinander getrennt sind. Die Zeichenfolgenwerte, die an die MyTagValue-Prozedur übergeben werden, entsprechen den Knoten, die von der XPath-Abfrage zurückgegeben werden. Die RunXPathQueryForSavedThreeTierXmlDocument-Prozedur beginnt mit der Instantiierung eines XML-Dokuments, xdc1. Danach wird die zuvor abgespeicherte Datei myCustomersSchema.xml geladen. Die Prozedur verwendet ein XmlTextReader-Objekt, um auf das XML-Dokument in der Datei myCustomersSchema.xml zuzugreifen, den Stammknoten zu öffnen und die Daten aus der Datei in das Objekt xdc1 zu laden. Nach dem Laden des zuvor abgespeicherten XML-Dokumentes führt das Beispiel die gleiche XPathAbfrage aus, wie das vorangegangene Beispiel. Die Syntax der XPath-Abfrage aus diesem Beispiel ist zwar mit dem vorangegangenen Beispiel identisch, die Quelle der Abfrage unterscheidet sich aber in mehreren wichtigen Gesichtspunkten. Erstens, die Quelle dieses Beispiels erfordert keine Zugriffe auf den Datenbankserver, denn es wird mit einer lokal abgespeicherten Datei gearbeitet, die das XMLDokument enthält. Wenn der Datenbankserver oder die Verbindung temporär nicht zur Verfügung stehen, kann diese lokale Ressource die Robustheit der Anwendung wesentlich verbessern. Zweitens, es gibt kein Dataset für das zugrunde liegende XML-Dokument. Dies bedeutet, dass die von der XPath-Abfrage zurückgelieferten Knoten Zeichenfolgen ohne dazugehörige Zeilenstruktur darstellen. Dementsprechend muss die Prozedur die Elemente aus den Knoten anders verarbeiten, als das vorangegangene Beispiel. Diese Prozedur generiert eine identische Ausgabe, die in Abbildung 12.9 dargestellt ist. Die ausgegebenen Informationen werden jedoch auf andere Weise zusammengestellt, als im vorigen Beispiel. Der neue Ansatz der Extrahierung von Tagwerten ist erforderlich, denn es gibt keine zugrunde liegende Zeilenstruktur eines DataSets, um die Extrahierung der Werte zu vereinfachen. Jeder Knoten aus dem Resultset der XPath-Abfrage aus diesem Beispiel wird als Zeichenfolge dargestellt. Tags trennen die Tagwerte voneinander in jeder Zeichenfolge ab. In Abbildung 12.8 können Sie erkennen, dass die Tags <CustomerID> und </CustomerID> den Tagwert ALFKI einschließen. Sie können also jeden Tagwert über die Angabe des anführenden und abschließenden Tags ermitteln. Unter Verwendung der Mid-Funktion, können Sie die in den Tags enthaltenen Tagwerte extrahieren. Die Prozeduren RunXPathQueryForSavedThreeTierXmlDocument und MyTagValue arbeiten zusammen, um die ersten vier Tagwerte für jeden einzelnen Knoten aus dem Resultset der XPath-Abfrage zu extrahieren. Die RunXPathQueryForSavedThreeTierXmlDocument-Prozedur übergibt den Tagnamen jedes der vier ersten Tags eines jeweiligen Knotens und die MyTagValue-Prozedur liefert die Zeichenfolge des dazugehörigen Tagwertes zurück. Danach fasst die RunXPathQueryForSavedThreeTierXmlDocument-Prozedur die Tagwerte zusammen und gibt diese im Ausgabefenster aus. Sub RunXPathQueryForSavedThreeTierXmlDocument() ' Die Prozedur verwendet ein gespeichertes Dokument ' anstatt das Dokument mit einem neuen Dataset neu ' zu erstellen. ' Deklarieren und Instantiieren eines XML-Dokumentes. Dim xdc1 As New System.Xml.XmlDocument() ' Deklarieren und Instantiieren eines Reader-Objekts auf ' der Grundlage eines zuvor gespeicherten XML-Dokuments; ' wechseln zum Stammelement des Dokuments und laden ' der Daten in xdc1. Dim xrd1 As XmlTextReader = _ New XmlTextReader _ ("c:\SQL Server Development with VBDotNet\" & _ 488 Kapitel 12 677.book Page 489 Thursday, December 5, 2002 2:26 PM "Chapter12\myCustomersSchema.xml") xrd1.MoveToContent() xdc1.Load(xrd1) ' Schließen des XmlTextReader-Objekts. xrd1.Close() ' Generieren eines Resultsets mit allen Kunden, die Produkte ' mit einem UnitPrice von größer als 100 bestellt haben. Dim xnl1 As XmlNodeList = _ xdc1.DocumentElement.SelectNodes( _ "descendant::Customers" & _ "[Orders/OrderDetails/UnitPrice>100]") ' Deklarieren der Objekte für eine Schleife durch das Resultset. Dim xnd1 As XmlNode Dim str1, str2 As String ' Durchlaufen des Resultsets und Ausgeben der ' Werte im Ausgabefenster. Debug.WriteLine("Es gibt " & _ xnl1.Count.ToString & " in diesem Resultset.") For Each xnd1 In xnl1 ' Die inneren XML-Daten des Knotens. str1 = xnd1.OuterXml ' Den CustomerID-Tagwert bestimmen. str2 = MyTagValue("CustomerID", str1) ' Den CompanyName-Tagwert bestimmen. str2 = str2 & ", " & MyTagValue("CompanyName", str1) ' Den ContactName-Tagwert bestimmen. str2 = str2 & ", " & MyTagValue("ContactName", str1) ' Den Phone-Tagwert bestimmen. str2 = str2 & ", " & MyTagValue("Phone", str1) ' Ausgeben der ersten vier Tagwerte. Debug.WriteLine(str2) Next End Sub Function MyTagValue(ByVal TagName As String, _ ByVal strXML As String) ' Deklarieren und Zusammenstellen der ' Konstanten für den jeweiligen Tag. Dim str1 = "<" & TagName & ">" Dim str2 = "</" & TagName & ">" Dim int1, int2 As Integer int1 = InStr(strXML, str1) + Len(str1) int2 = InStr(strXML, str2) Programmieren von XML mit Visual Basic .NET 489 677.book Page 490 Thursday, December 5, 2002 2:26 PM ' Ermitteln und Zurückgeben des Tagwerts; ' strXML ist die Zeichenfolge mit den ' auszuwertenden XML-Daten, ' int1 ist die Startposition, ' int2 - int1 ist die Anzahl der Zeichen. Dim TagValue As String = Mid(strXML, _ int1, int2 - int1) Return TagValue End Function Verwenden von DataSets zur Aktualisierung von Datenbanken über Diffgramme An dieser Stelle sollten Sie verstanden haben, dass Sie mit DataSets und XML-Dokumenten Datenbankoperationen ausführen und identische Ergebnisse erhalten können. Diese allgemeine Regel trifft auch auf Datenbankaktualisierungen zu. Wie bereits früher in diesem Kapitel erwähnt, aktualisiert ADO.NET Datenbanken mithilfe von Diffgrammen, welche XML-Dokumente darstellen, die aktuelle und vorherige Spaltenwerte aus einem DataTable-Objekt eines DataSets separat angeben können. Wenn eine ADO.NET-Anwendung die Update-Methode eines DataAdapter-Objekts aufruft und ein Dataset angibt, sendet die Anwendung ein Diffgramm an das auf einem Server ausgeführte .NET Framework. Das .NET Framework versucht wiederum die Aktualisierung mit dem Datenbankserver durchzuführen und gibt alle erforderlichen Rückgabeinformationen an den Client zurück, z. B. einen Identity-Wert oder eine Benachrichtigung, dass die Datenbank die Aktualisierung zurückwies, weil sich ein vorheriger Wert seit der Zeit, da das Dataset ursprünglich gefüllt wurde, geändert hat. Das Beispiel aus diesem Abschnitt interagiert mit XML auf zwei verschiedenen Wegen. Erstens, es wird ein kommentiertes Schema verwendet, um die Spaltenwerte anzugeben, die von der Remotedatenquelle zurückgegeben werden sollen. Nach der Abfrage der Werte von der Remotedatenquelle füllt das Beispiel ein DataTable-Objekt im Dataset auf dem Clientcomputer. Zweitens, das Beispiel aktualisiert einen Spaltenwert im lokalen DataTable-Objekt. Danach generiert die Prozedur ein Diffgramm, das die Änderung enthält, bevor die Update-Methode des DataAdapter-Objektes aufgerufen wird, um das Diffgramm an den Datenbankserver zu senden. Es ist zwar möglich, mit Diffgrammen genauso wie mit Updategrammen (siehe Kapitel 6) direkt zu arbeiten, Visual Basic-Entwickler werden es jedoch wahrscheinlich allgemein als bequemer empfinden, das ADO.NET-Objektmodell einzusetzen, um die Werte lokal und auf dem Remoteserver zu aktualisieren. Das folgende Schemalisting zeigt den Inhalt der Datei EmployeesFirstLastNames.xsd, die im Beispiel zu diesem Abschnitt verwendet wird. Die Datei befindet sich im Stammordner des XMLSamples-Projekts. (Die Zeilen für die Elemente FName und LName wurden mit einem Zeilenumbruch auf zwei Zeilen dargestellt, denn diese sind für eine einzige Zeile zu lang.) Nach der Deklaration des Namespaces für das W3C-konforme XSD-Schema und die Zuordnung von Microsoft-Attributen deklariert das Listing den Namen Emp für das Employees-Objekt auf der Datenbankverbindung. Das sql:relation-Attribut stellt die Zuordnung zwischen Emp und Employees her. Da das Beispiel auf die Northwind-Datenbank zugreift, entspricht der Name Emp der Ansammlung abgefragter Werte aus der Employees-Tabelle. Das Schema gibt FName und LName als Namen im lokalen Dataset an, die den Spaltenwerten FirstName und LastName aus der Employees-Tabelle auf dem Datenbankserver zugeordnet sind. Das Attribut sql:field gibt die serverseitigen Spalten an, auf die die Spalten des lokalen DataSets verweisen. 490 Kapitel 12 677.book Page 491 Thursday, December 5, 2002 2:26 PM <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sql="urn:schemas-microsoft-com:mapping-schema"> <xsd:element name="Emp" sql:relation="Employees"> <xsd:complexType> <xsd:sequence> <xsd:element name="FName" _ sql:field="FirstName" type="xsd:string" /> <xsd:element name="LName" _ sql:field="LastName" type="xsd:string" /> </xsd:sequence> <xsd:attribute name="EmployeeID" type="xsd:integer" /> </xsd:complexType> </xsd:element> </xsd:schema> Die folgende PopulateModifyUpdateWithDiffGram-Prozedur beginnt mit der Angabe einer Verbindungszeichenfolge. Die Verbindungszeichenfolge wird dann verwendet, um ein SqlXmlCommandObjekt zu konstruieren. Die Informationen aus der Verbindungszeichenfolge verweisen auf die Northwind-Datenbank auf der lokalen Standardinstanz von SQL Server. Als nächstes erstellt die Prozedur ein lokales Dataset (das1) mit einer Datentabelle namens Emp auf der Grundlage der Schemadatei EmployeesFirstLastNames.xsd. Dieses Dataset komplettiert die Einrichtung der Datenumgebung für das Beispiel. Sub PopulateModifyUpdateWithDiffGram() ' Angeben einer Verbindung für das SqlXmlCommand-Objekt cmd1; ' Die Angabe der Verbindung muss den Provider (sqloledb) umfassen. Dim cmd1 As New SqlXmlCommand("Provider=sqloledb;" & _ "Data Source=(local);" & _ "Initial Catalog=northwind;Integrated Security=SSPI") ' Angeben des SqlXmlCommand-Objekts zur Rückgabe ' des Vor- und Zunamens auf der Grundlage einer XPath-Abfrage. cmd1.RootTag = "ROOT" cmd1.CommandText = "Emp" cmd1.CommandType = SqlXmlCommandType.XPath cmd1.SchemaPath = "..\EmployeesFirstLastNames.xsd" ' Instantiieren eines SqlXmlAdapter-Objektes unter Verwendung ' des SqlXmlCommand-Objekts. Dim dap1 As SqlXmlAdapter dap1 = New SqlXmlAdapter(cmd1) ' Instantiieren eines neuen DataSet-Objekts (das1) und ' Füllen des DataSets via dap1. Dim das1 As DataSet = New DataSet() dap1.Fill(das1) ' Editieren des Wertes aus der ersten Spalte der ersten Zeile ' aus dem DataTable-Objekt Emp. das1.Tables("Emp").Rows(0)(0) = "Nancie" ' Schreiben der XML-Daten als Diffgramm, bevor die ' Änderungen an den Server übermittelt werden. Dim str1 As String = _ "c:\SQL Server Development with VBDotNet\" & _ Programmieren von XML mit Visual Basic .NET 491 677.book Page 492 Thursday, December 5, 2002 2:26 PM "Chapter12\myDiffGram.xml" Dim myFileStream As New System.IO.FileStream _ (str1, System.IO.FileMode.Create) Dim xtw1 As New System.Xml.XmlTextWriter _ (myFileStream, System.Text.Encoding.Unicode) das1.WriteXml(xtw1, XmlWriteMode.DiffGram) ' Ausführen der Aktualisierung der serverseitigen ' Datenquelle des DataSets das1; geben Sie keine ' spezifische Datentabelle im Dataset an. dap1.Update(das1) End Sub Nach der Einrichtung der Datenumgebung weist die Prozedur der ersten Spalte aus der ersten Zeile aus dem DataTable-Objekt Emp einen neuen Wert von Nancie zu. Die Rows-Ansammlung des DataTable-Objekts Emp ermöglicht den Zugriff auf die Spaltenwerte der einzelne Zeilen innerhalb des DataTable-Objekts. In der folgenden Zeile, das1.Tables("Emp").Rows(0)(0) = "Nancie" bezeichnet der erste Zähler in Klammern nach dem Rows-Schlüsselwort die Zeile und der zweite Zähler in Klammern die Spalte innerhalb der Zeile. (Die Rows-Ansammlung basiert auf dem Wert 0 für die erste Spalte und die erste Zeile.) Bevor die Aktualisierung der Northwind-Datenbank mit der Update-Methode des DataAdapterObjektes vorgenommen wird, kopiert die Prozedur das Dataset im Diffgrammformat in eine Datei namens C:\SQL Server Development With VBDotNet\Chapter12\MyDiffGram.xml auf dem Laufwerk C: des lokalen Computers. Sie können den Namen und das Zielverzeichnis ändern, um Ihrer Computerkonfiguration zu entsprechen. Abbildung 12.10: Der obere Teil aus der Datei myDiffGram.xml, die von der PopulateModifyUpdateWith DiffGram-Prozedur generiert wird 492 Kapitel 12 677.book Page 493 Thursday, December 5, 2002 2:26 PM Die Abbildung 12.10 und die Abbildung 12.11 stellen das mit dem Beispiel aus diesem Abschnitt in der Datei MyDiffGram.xml erstellte Diffgramm dar. Die Abbildung 12.10 stellt ein Browserfenster dar, das die obere Hälfte des Diffgramms zeigt und die Abbildung 12.11 zeigt die untere Hälfte des Diffgramms im Browserfenster. Wie die Abbildung 12.10 verdeutlicht, lautet der Wert des FNameTags der Angestellten mit einem EmployeeID-Wert von 1 Nancie (siehe den oberen Bereich des Fensters). In Abbildung 12.11 zeigt der before-Abschnitt des Diffgramms (siehe den unteren Bereich des Fensters) den ursprünglichen Wert aller Änderungen aus dem Dataset, die noch nicht von der Remotedatenquelle übernommen wurden. Im vorliegenden Beispiel können Sie erkennen, dass der ursprüngliche Wert des FName-Tags der Angestellten mit einem EmployeeID-Wert von 1 Nancy lautet. Unmittelbar nach dem Aufruf der Update-Methode in der letzten Zeile der PopulateModifyUpdateWithDiffGram-Prozedur ändert sich das Diffgramm von das1. Insbesondere wird der beforeAbschnitt gelöscht, denn das Dataset enthält nun nur noch aktuelle Werte, bis erneut eine Veränderung am lokalen DataTable-Objekt Emp vorgenommen wird. Abbildung 12.11: Der untere Teil aus der Datei myDiffGram.xml, die von der PopulateModifyUpdateWith DiffGram-Prozedur generiert wird Sie werden wahrscheinlich die Employees-Tabelle aus der Northwind-Datenbank wiederherstellen wollen, damit der Vornahme der EmployeeID 1 wieder Nancy statt Nancie lautet. Sie können dies erreichen, wenn Sie Nancie in der Prozedur PopulateModifyUpdateWithDiffGram in Nancy ändern und die Prozedur noch einmal ausführen. HINWEIS: Wie ich bereits erwähnt habe werden es viele Visual Basic .NET-Entwickler vorteilhaft finden, Datenverarbeitungsaufgaben über DataSet-Objekte auszuführen, statt Diffgramme oder Updategramme direkt zu kodieren. Die Beispieldateien zu diesem Buch umfassen eine weitere Beispielprozedur, um die Flexibilität und Einfachheit dieser Vorgehensweise noch einmal zu verdeutlichen. Aus Gründen der Kürze wird das Listing zu dieser Prozedur nicht im Buch aufgeführt. Programmieren von XML mit Visual Basic .NET 493 677.book Page 494 Thursday, December 5, 2002 2:26 PM Verwenden von Diffgrammen im Web ohne virtuelle Verzeichnisse Eines der herausstechendsten Merkmale des vorigen Beispiels ist dessen Robustheit. Beispielsweise kann ziemlich genau der gleiche Code in ASP.NET-Anwendungen verwendet werden. Darüber hinaus kann eine ASP.NET-Anwendung Aktualisierungen über das Web unterstützen, ohne dass Sie hierfür ein virtuelles Verzeichnis für eine Datenbank einrichten müssen. Dadurch wird die Administration Ihrer Webanwendungen vereinfacht. Mit den folgenden fünf Schritten kann eine ASP.NET-Webanwendung namens XMLWebSample erstellt werden. Mit diesen Schritten wird das Beispiel aus dem vorangegangenen Abschnitt so angepasst, dass es in einer ASP.NET-Lösung eingesetzt werden kann. 1. Beginnen Sie ein neues ASP.NET-Projekt namens XMLWebSample und fügen Sie eine Referenz auf den Namespace Microsoft.Data.SqlXml wie weiter oben in diesem Kapitel beschrieben hinzu. 2. Wählen Sie die Standarddatei WebForm1.aspx in der Entwurfsansicht aus und öffnen Sie das Modul der Seite, indem Sie auf die Seite mit der rechten Maus klicken und den Befehl Code anzeigen wählen. Fügen Sie am oberen Ende des Moduls der Seite folgende Zeile ein: Imports Microsoft.Data.SqlXml. 3. Kopieren Sie den Code aus der Prozedur PopulateModifyUpdateWithDiffGram aus dem vorigen Projekt in die Page_Load-Ereignisprozedur der XMLWebSample-Anwendung. 4. Erstellen Sie im Stammordner des XMLWebSample-Projektes ein Schema, wie in EmployeesFirstLastNames.xsd. Sie können den XML-Designer für diese Aufgabe wie weiter oben in diesem Kapitel beschrieben verwenden. (Es ist wahrscheinlich am einfachsten, das Schema in der XMLQuellansicht zu öffnen und die existierenden XML-Daten mit den XML-Daten aus der Datei EmployeesFirstLastNames.xsd aus den Beispieldateien zu diesem Buch zu ersetzen.) Nennen Sie das Schema EmployeesFirstLastNames.xsd. 5. Ändern Sie die Einstellung der SchemaPath-Eigenschaft des SqlXmlCommand-Objekts in der Page_Load-Ereignisprozedur von "..\EmployeesFirstLastNames.xsd" zu MapPath("EmployeesFirstLastNames.xsd"). Nach Abschluss dieser Schritte können Sie auf die Seite WebForm1.aspx mit der rechten Maustaste im Projektmappen-Explorer klicken und den Befehl Erstellen und durchsuchen wählen. Dieser Prozess setzt das FirstName-Feld der Zeile aus der Employees-Tabelle mit einem EmployeeID-Wert von 1 auf Nancie. Sie können die ursprünglichen Vornamen wiederherstellen, wenn Sie Nancie in Nancy ändern und den Befehl Erstellen und durchsuchen ein weiteres Mal wählen. Zur leichteren Referenz wird die Page_Load-Ereignisprozedur an dieser Stelle aufgeführt. Die beiden Zeilen, die sich in Bezug auf die PopulateModifyUpdateWithDiffGram-Prozedur geändert haben, sind fett dargestellt. Der wichtigste Punkt ist, dass das folgende Listing zwar für ASP.NET vorgesehen ist, aber fast identisch zur früheren Windows-Anwendung funktioniert. Die MapPath-Funktion gibt den vollständigen Pfad auf die Datei zurück, die im Argument angegeben wird. Diese Webfunktion gibt dem Entwickler die Möglichkeit, einen Pfad auf die Datei zu verwenden, ohne diesen in der Anwendung explizit angeben zu müssen. Außerdem verbessert die MapPath-Funktion die Portabilität Ihres Codes, denn die Funktion berechnet den Pfad auf die Datei dynamisch, selbst wenn Sie den Ordner der Anwendung ändern. Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Angeben einer Verbindung für das SqlXmlCommand-Objekt cmd1; 494 Kapitel 12 677.book Page 495 Thursday, December 5, 2002 2:26 PM ' Die Angabe der Verbindung muss den Provider (sqloledb) umfassen. Dim cmd1 As New SqlXmlCommand("Provider=sqloledb;" & _ "Data Source=(local);" & _ "Initial Catalog=northwind;Integrated Security=SSPI") ' Angeben des SqlXmlCommand-Objekts zur Rückgabe ' des Vor- und Zunamens auf der Grundlage einer XPath-Abfrage. cmd1.RootTag = "ROOT" cmd1.CommandText = "Emp" cmd1.CommandType = SqlXmlCommandType.XPath cmd1.SchemaPath = MapPath("EmployeesFirstLastNames.xsd") ' Instantiieren eines SqlXmlAdapter-Objektes unter Verwendung ' des SqlXmlCommand-Objekts. Dim dap1 As SqlXmlAdapter dap1 = New SqlXmlAdapter(cmd1) ' Instantiieren eines neuen DataSet-Objekts (das1) und ' Füllen des DataSets via dap1. Dim das1 As DataSet = New DataSet() dap1.Fill(das1) ' Editieren des Wertes aus der ersten Spalte der ersten Zeile ' aus dem DataTable-Objekt Emp. das1.Tables("Emp").Rows(0)(0) = "Nancie" ' Schreiben der XML-Daten als Diffgramm, bevor die ' Änderungen an den Server übermittelt werden. Dim str1 As String = _ "c:\SQL Server Development with VBDotNet\" & _ "Chapter12\myDiffGram.xml" Dim myFileStream As New System.IO.FileStream _ (str1, System.IO.FileMode.Create) Dim xtw1 As New System.Xml.XmlTextWriter _ (myFileStream, System.Text.Encoding.Unicode) das1.WriteXml(xtw1, XmlWriteMode.DiffGram) ' Ausführen der Aktualisierung der serverseitigen ' Datenquelle des DataSets das1; geben Sie keine ' spezifische Datentabelle im Dataset an. dap1.Update(das1) End Sub Erstellen von HTML-Seiten mit XSLT Wenn Sie mit der Programmierung mit Visual Basic .NET beginnen, sollte sich Ihre Arbeit als Webentwickler auf ASP.NET konzentrieren (siehe Kapitel 11). Diese Technologie wurde speziell so ausgelegt, dass Visual Basic-Entwickler mit der Erstellung von Webanwendungen unmittelbar beginnen können. Wie Sie aus den vorangegangenen beiden Beispielen ersehen konnten, ist es einfach Visual Basic-Code in ASP.NET zu übernehmen. Gelegentlich könnte es allerdings sinnvoll sein, in der Webumgebung auszugebende Informationen unter Verwendung von XSLT zu generieren. Meiner Erfahrung entsprechend findet XSLT besonders bei der Umsetzung von XML-Dokumenten in Tabellen auf HTML-Seiten gerne Verwendung. Bis zu diesem Punkt zielte dieses Kapitel darauf ab, prakti- Programmieren von XML mit Visual Basic .NET 495 677.book Page 496 Thursday, December 5, 2002 2:26 PM sche Kenntnisse zur Erstellung und Verarbeitung von XML-Dokumenten in .NET-Anwendungen zu vermitteln. Der Rest dieses Kapitels widmet sich nun der Vorbereitung von XML-Dokumenten für die Darstellung auf HTML-Seiten mittels XSLT. Wenn Sie XSLT verwenden, um XML-Dokumente in HTML-Seiten zu transformieren, ist es dienlich, praktische Kenntnisse zur Syntax der HTML-Formatierung sowie zu kaskadierenden Stylesheets zu besitzen. Sie müssen natürlich auch wissen, wie auf die Tags in einem XML-Dokument zugegriffen werden kann, um diese in Ihren HTML-Seiten darzustellen. Viele Visual Basic-Entwickler besitzen nur geringe Erfahrung mit der HTML-Programmierung. Wenn dies auf Sie zutrifft, empfehle ich Ihnen eine Reihe von Strategien. Verwenden Sie einen grafischen Webseitendesigner, z. B. denjenigen aus .NET oder FrontPage. Mit einem grafischen Webseitendesigner können Sie Seiten auf grafische Art und Weise erstellen und dann den HTML-Code der Seiten analysieren. Sie können diesen Code dann in Ihre XSLT-Übersetzungsdatei übernehmen. Zweitens, wenn Sie Mitglied eines Projektteams sind, zu dem auch Webspezialisten gehören, sollten Sie das Projekt so planen, dass die Webspezialisten allgemeine .xslt-Dateien erstellen, die Sie in vielen Situationen und mit Leichtigkeit anpassen können. Die Visual Basic-Entwickler können dann auf die XSLT-Übersetzungsdateien im vorliegenden Format oder mit geringfügigen Anpassungen verweisen. Die Dokumentation zu Visual Studio .NET umfasst mehrere Beispiele, die verdeutlichen, wie XMLDokumente geladen und mit XSLT umgewandelt werden können (siehe das Thema Die XslTransform.Load-Methode (XmlReader) aus der Visual Basic .NET-Dokumentation für ein Beispiel). Der aktuelle Abschnitt aus diesem Buch umfasst eine Reihe von Beispielen, die diejenigen aus der Visual Basic .NET-Dokumentation, die mit der SqlXmlCommand-Klasse arbeiten, vervollständigen. Erinnern Sie sich, dass Sie diese SQLXML-verwaltete Klasse verwenden können, um XML-Dokumente für SQL-Anweisungen zu generieren. Die SQLXML-verwalteten Klassen wurden bereitgestellt, um SQL Server-Entwickler zu unterstützen. Um ein Beispiel zugeben, die SchemaPath-Eigenschaft erleichtert die Referenzierung eines kommentierten Schemas zur Filterung eines Resultsets von einem Datenbankobjekt. Auf ähnliche Weise referenziert die XslPath-Eigenschaft eines SqlXmlCommandObjekts eine .xslt-Datei. Wenn Sie dieses Attribut angeben, können Ihre Prozeduren HTML-Seiten statt unverarbeiteter und unformatierter XML-Tags und -Werte aus einer Dokumentdatei zurückgeben. Die angegebene XSLT-Übersetzungsdatei muss mit dem XML-Dokument, das anderenfalls vom SqlXmlCommand-Objekt zurückgeliefert wird, synchronisiert werden. Zwei Beispiel-XSLT-Übersetzungsdateien verdeutlichen, wie diese Synchronisation implementiert wird. Formatieren zweier Spalten aus der Employees-Tabelle Wenn Sie die XslPath-Eigenschaft eines SqlXmlCommand-Objekts verwenden, greifen Sie auf das zugrunde liegende XML-Dokument nicht direkt zu. Der interne Code der SqlXmlCommand-Klasse konvertiert das XML-Dokument des Objekts automatisch in HTML-Code entsprechend den Anweisungen aus der Datei, auf die die XslPath-Eigenschaft verweist. Das folgende Beispiel übersetzt ein XML-Dokument auf der Grundlage der Employees-Tabelle aus der Northwind-Datenbank. Statt nur die endgültige HTML-Seite zu speichern, speichert die Prozedur zuerst das XML-Dokument ohne Angabe der XslPath-Eigenschaft ab. Danach weist die Prozedur der XslPath-Eigenschaft eine Zeichenfolge zu, die auf eine .xslt-Datei verweist, und speichert das zweite Dokument im HTML-Format. Die Prozedur SQLToXMLToHTMLForEmployees beginnt mit der Erstellung eines XML-Dokumentes mithilfe eines SqlXmlCommand-Objekts, das auf die Northwind-Datenbank verweist. Die SQLZeichenfolge für das Objekt extrahiert die Spalten EmployeeID, FirstName und LastName aus der Employees-Tabelle unter Verwendung einer SELECT-Anweisung mit einer FOR XML-Klausel. Denken Sie daran, dass dieser Prozess ein XML-Fragment ohne eindeutigen äußeren Tag für das Doku- 496 Kapitel 12 677.book Page 497 Thursday, December 5, 2002 2:26 PM ment zurückliefert. Die Prozedur weist aus diesem Grunde der RootTag-Eigenschaft des SqlXmlCommand-Objekts den Zeichenfolgenwert (“MyRoot”) zu. Als nächstes wird die Prozedur für die Abspeicherung des XML-Dokumentes in einer Datei namens UnformattedEmployees.xml vorbereitet, bevor die ExecuteToStream-Methode aufgerufen wird, um das XML-Dokument zu speichern. Die Vorbereitungsmaßnahmen ermöglichen es der ExecuteToStream-Methode, das Dokument direkt aus dem SqlXmlCommand-Objekt an eine Datei zu übergeben. Nach der Abspeicherung des XMLDokuments setzt die Prozedur die XslPath-Eigenschaft des SqlXmlCommand-Objekts. Die Eigenschaft verweist auf die Datei MyXSL.xslt im Stammordner des XMLSample-Projektordners. Danach ruft die Prozedur die ExecuteStream-Methode des SqlXmlCommand-Objekts auf, um die HTMLSeite im Arbeitsspeicher mit einem Stream-Objekt zu repräsentieren. Nach der Erfassung der HTMLDaten in Form eines Stream-Objekts setzt die Prozedur mit dem Auslesen des Stream-Objekts fort und schreibt diese Informationen in eine externe Datei namens FormattedEmployees.html. Sub SQLToXMLToHTMLForEmployees() ' Angeben eines SqlXmlCommand-Objekts. Dim cmd1 As New SqlXmlCommand("Provider=sqloledb;" & _ "Data Source=(local);" & _ "Initial Catalog=northwind;Integrated Security=SSPI") cmd1.CommandText = _ "SELECT EmployeeID, FirstName, LastName " & _ "FROM Employees FOR XML AUTO" cmd1.CommandType = SqlXmlCommandType.Sql cmd1.RootTag = "MyRoot" ' Angeben des Pfades und der Datei für das XML-Resultset, ' danach instantiieren eines Stream-Objekts ' für die Inhalte der Datei. Dim myXMLfile As String = _ "c:\SQL Server Development with VBDotNet\" & _ "Chapter12\UnFormattedEmployees.xml" Dim myFileStream As New System.IO.FileStream _ (myXMLfile, System.IO.FileMode.Create) ' Ausführen von cmd1 und Speichern ' des Resultsets im Stream-Objekt. cmd1.ExecuteToStream(myFileStream) ' Schließen des FileStream-Objekts, um die Ressourcen freizugeben. myFileStream.Close() ' Setzen der XslPath-Eigenschaft, um ' den Namen des XSLT-Stylesheets anzugeben. cmd1.XslPath = "..\MyXSL.xslt" ' Rückgabe der HTML-Daten von cmd1 als Stream-Objekt ' im Arbeitsspeicher; danach erstellen eines StreamReaders, ' um die Inhalte aus dem Stream-Objekt auszulesen. Dim stm1 As Stream stm1 = cmd1.ExecuteStream Dim srd1 As New StreamReader(stm1) ' Deklarieren und Instantiieren einer Zeichenfolge für ' den Namen der Datei, auf die das FileStream-Objekt mit ' den HTML-Inhalten verweisen soll. Programmieren von XML mit Visual Basic .NET 497 677.book Page 498 Thursday, December 5, 2002 2:26 PM Dim str1 As String = _ "c:\SQL Server Development with VBDotNet\" & _ "Chapter12\FormattedEmployees.html" Dim fst1 As New FileStream(str1, FileMode.OpenOrCreate) ' Deklarieren und Instantiieren eines StreamWriter-Objekts, ' um die Datei mit den HTML-Inhalten zu füllen; danach auslesen ' der StreamReader-Inhalte in eine Zeichenfolge und schreiben der ' Zeichenfolge in fst1. Dim swt1 As New StreamWriter(fst1) Dim str2 As String = srd1.ReadToEnd swt1.Write(str2) ' Schließen der Datei. swt1.Close() End Sub Die Abbildung 12.12 zeigt die Datei UnFormattedEmployees.xml. Beachten Sie, dass diese Datei neun Employee-Elemente umfasst. Jedes Element verfügt über drei Attribute mit Werten für EmployeeID, FirstName und LastName. Der Inhalt und das Layout folgen direkt aus der CommandTextEigenschaftszuweisung für das SqlXmlCommand-Objekt aus der Prozedur SQLToXMLToHTMLForEmployees. Die Datei UnFormattedEmployees.xml wird mit der Datei MyXSL.xslt übersetzt. Abbildung 12.12: Die Inhalte aus der Datei UnFormattedEmployees.xml, die mit der Prozedur SQLToXMLToHTMLForEmployees generiert wurden Die Abbildung 12.13 zeigt das übersetzte XML-Dokument, das mit dem Namen FormattedEmployees.html abgespeichert wurde. Darüber hinaus wird die zweite Spalte zur Anzeige des Nachnamens in Kursiv- und Fettschrift dargestellt. Es gibt noch weitere Formatierungen, z. B. einen Tabellenkopf mit Hintergrundfarbe. Die Datei MyXSL.xslt bewirkt sämtliche Layout- und Formatierungsänderungen, die zwischen Abbildung 12.12 und Abbildung 12.13 zu erkennen sind. Es gibt noch einen Unterschied zwischen diesen beiden Abbildungen. Die Abbildung 12.13 verfügt lediglich über zwei Spalten, das ursprüngliche XML-Dokument enthält jedoch drei Attribute für jedes Employee-Element im Dokument. Dieser Unterschied beruht auf der Tatsache, dass die Datei MyXSL.xslt nur zwei der drei Attribute zur Anzeige auswählt. 498 Kapitel 12 677.book Page 499 Thursday, December 5, 2002 2:26 PM Abbildung 12.13: Die Inhalte aus der Datei FormattedEmployees.html, die mit der Prozedur SQLToXMLToHTMLForEmployees generiert wurden Das Listing für die Datei MyXSL.xslt wird als nächstes dargestellt. Diese Datei beginnt mit einer Deklarierung als XML-Dokument und einer Referenz auf den Namespace des World Wide Web Consortium für XSLT-Dateien. Das Design der Umwandlungsdatei besteht aus zwei Hauptteilen, die mit zwei xsl:template-Elementen bezeichnet wurden. Das erste Element stimmt mit dem Employees-Element aus dem XML-Quelldokument, namentlich UnformattedEmployees.xml, überein. Für jedes Employees-Element aus dem Quelldokument wählt die Übersetzungsdatei zwei Attribute aus – FirstName und LastName. Die LastName-Auswahl ist in zwei Tags eingebettet, die den Attributswert in Fett- und Kursivschrift darstellen. Dieses erste Segment der Datei definiert darüber hinaus das Zeilenlayout für die Ergebnisse mit <TR>-Anfangs- und -End-Tags. Jeder ausgewählte Wert erscheint mit <TD>-Anfangs- und -End-Tag, um anzuzeigen, dass die Werte unterschiedliche Zellen in der Zeile belegen. Das zweite xsl:template-Element aus der .xslt-Datei definiert den Rumpf des Dokumentes. Dieses Element beginnt beispielsweise mit einem HTML-Anfangselement und wird mit einem HTML-Endelement beendet. Der HEAD-Block weist dem Tabellenkopfelement (th) in hexadezimaler Schreibweise mithilfe des background-color-Attributs eine Farbe zu. Der BODY-Block umfasst einen TABLE-Block und formatiert den Tabellenkopf. Das xsl:apply-templates-Element innerhalb des TABLE-Blocks bewirkt die Einfügung des ersten xsl:template-Elements in das zweite xsl:template-Element. Über diese Einfügung werden der Tabelle nach dem Tabellenkopf Zeilen hinzugefügt. Es ist wichtig, dass das xsl:apply-templates-Element das Element MyRoot im XML-Dokument angibt, denn dieses Element beinhaltet sämtliche Employees-Elemente aus dem XML-Quelldokument, die übersetzt werden sollen. Geben Sie dieses Auswahlattribut nicht ordnungsgemäß an, könnte die Tabelle leer dargestellt werden. HINWEIS: Die XSLT-Syntax bezieht sich auf Element- und Attributsnamen unterschiedlich. Wenn Sie einen Verweis auf ein Element angeben wollen, können Sie dessen Namen verwenden. Verwenden Sie beispielsweise Employees, um ein Employees-Element zu referenzieren. Wenn Sie sich hingegen auf ein Attribut beziehen, müssen Sie dem Attributsnamen ein @-Zeichen voranstellen. Beziehen Sie sich beispielsweise mit @FirstName auf einen FirstName-Attributswert. Programmieren von XML mit Visual Basic .NET 499 677.book Page 500 Thursday, December 5, 2002 2:26 PM <?xml version='1.0' encoding='UTF-8'?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/ Transform" version="1.0"> <xsl:template match = 'Employees'> <TR> <TD><xsl:value-of select = '@FirstName' /></TD> <TD><B><I><xsl:value-of select = '@LastName' /></I></B></TD> </TR> </xsl:template> <xsl:template match = '/'> <HTML> <HEAD> <STYLE>th { background-color: #CCCCCC }</STYLE> </HEAD> <BODY> <TABLE border='1' style='width:300;'> <TR><TH colspan='2'>Angestellte(r)</TH></TR> <TR><TH >Vorname</TH><TH>Nachname</TH></TR> <xsl:apply-templates select = 'MyRoot' /> </TABLE> </BODY> </HTML> </xsl:template> </xsl:stylesheet> Formatieren dreier Spalten aus der Shippers-Tabelle Das letzte Beispiel aus diesem Kapitel demonstriert die vorangegangene Vorgehensweise mit einem anderen XML-Quelldokument. Die XSLT-Übersetzung wird zwar auf ein XML-Dokument angewendet, allerdings werden Sie das der veröffentlichten HTML-Tabelle zugrunde liegende XML-Dokument normalerweise nicht anzeigen. Stattdessen werden Sie die XslPath-Eigenschaft des SqlXmlCommand-Objekts unmittelbar angeben, wenn Sie die ExecuteStream-Methode aufrufen. Danach können Sie den Strom der HTML-Daten auslesen, um die Abspeicherung der Daten in einer Datei vorzubereiten. Bei dieser direkteren Vorgehensweise wird das XML-Dokument, das als Quelle der HTML-Tabelle dient, nicht offen gelegt. Wenn Sie das XML-Dokument jemals analysieren wollen, können Sie jederzeit auf die Technik aus dem vorangegangenen Beispiel zurückgreifen. Das Beispiel, das mit der SQLThroughXMLToHTMLForShippers-Prozedur implementiert wurde, extrahiert Spalten aus der Shippers-Tabelle. Die SELECT-Anweisung für das SqlXmlCommandObjekt fragt alle Spalten aus der Shippers-Tabelle ab. Die Anweisung umfasst eine FOR XML-Klausel, um ein XML-Fragment zurückzuliefern. Die RootTag-Eigenschaft des SqlXmlCommandObjekts bezeichnet MyRoot als eindeutiges Element, das alle anderen Elemente im XML-Dokument umfasst. Die XslPath-Eigenschaft des SqlXmlCommand-Objekts verweist auf die Datei MyXSLShippers.xslt im Stammverzeichnis des Ordners \XMLSamples. Dies ist der Ordner, in dem das Projekt abgespeichert wurde. Nach der Instantiierung des SqlXmlCommand-Objektes und der Angabe der Eigenschaften dieses Objekts ruft die Prozedur die ExecuteStream-Methode auf, um im Arbeitsspeicher eine Stream-Variable mit den HTML-Daten für die Datei anzulegen, die von der Prozedur letztendlich als FormattedShippers.html abgespeichert wird. 500 Kapitel 12 677.book Page 501 Thursday, December 5, 2002 2:26 PM Sub SQLThroughXMLToHTMLForShippers() ' Angeben eines SqlXmlCommand-Objekts. Dim cmd1 As New SqlXmlCommand("Provider=sqloledb;" & _ "Data Source=(local);" & _ "Initial Catalog=northwind;Integrated Security=SSPI") cmd1.CommandText = _ "SELECT ShipperID, CompanyName, Phone " & _ "FROM Shippers FOR XML AUTO" cmd1.CommandType = SqlXmlCommandType.Sql cmd1.RootTag = "MyRoot" ' Setzen der XslPath-Eigenschaft, um ' den Namen des XSLT-Stylesheets anzugeben. cmd1.XslPath = "..\MyXSLShippers.xslt" ' Rückgabe der HTML-Daten von cmd1 als Stream-Objekt ' im Arbeitsspeicher; danach erstellen eines StreamReaders, ' um die Inhalte aus dem Stream-Objekt auszulesen. Dim stm1 As Stream stm1 = cmd1.ExecuteStream Dim srd1 As New StreamReader(stm1) ' Deklarieren und Instantiieren einer Zeichenfolge für ' den Namen der Datei, auf die das FileStream-Objekt mit ' den HTML-Inhalten verweisen soll. Dim str1 As String = _ "c:\SQL Server Development with VBDotNet\" & _ "Chapter12\FormattedShippers.html" Dim fst1 As New FileStream(str1, FileMode.OpenOrCreate) ' Deklarieren und Instantiieren eines StreamWriter-Objekts, ' um die Datei mit den HTML-Inhalten zu füllen; danach auslesen ' der StreamReader-Inhalte in eine Zeichenfolge und schreiben der ' Zeichenfolge in fst1. Dim swt1 As New StreamWriter(fst1) Dim str2 As String = srd1.ReadToEnd swt1.Write(str2) ' Schließen der Datei. swt1.Close() End Sub Die Abbildung 12.14 zeigt die von der Prozedur SQLThroughXMLToHTMLForShippers ausgegebenen Informationen. Der Titel Shippers erstreckt sich im Tabellenkopf dieses Beispiels über drei Spalten, im Gegensatz zu zwei Spalten im vorangegangenen Beispiel. Natürlich hat die Tabelle drei Spalten und die Inhalte der ersten Spalte, welche die ShipperID-Werte aufführt, wurden horizontal zentriert. Abgesehen von diesen Unterschieden entspricht das Layout der Tabelle dem Design des vorangegangenen Beispiels. Programmieren von XML mit Visual Basic .NET 501 677.book Page 502 Thursday, December 5, 2002 2:26 PM Abbildung 12.14: Die Inhalte der Datei FormattedEmployees.html, die mit der Prozedur SQLThroughXMLToHTMLForShippers generiert wurden Ich habe das XML-Dokument zwar nicht dargestellt, denn Sie werden dieses normalerweise bei der Erstellung einer HTML-Tabelle nicht anzeigen, es ist jedoch mit dem XML-Dokument aus Abbildung 12.12 ziemlich vergleichbar. Diese Abbildung zeit die XML-Ausgabe des vorigen Beispiels. Das Beispiel erstellte ein XML-Dokument mit der gleichen SELECT-Anweisung, die auch in diesem Beispiel verwendet wurde. Der Hauptunterschied zwischen den XML-Daten der beiden Dokumente ist wichtig, aber minimal. Die Abbildung 12.12 lässt erkennen, dass das Stammelement des XML-Dokumentes aus dem vorangegangenen Beispiel Shippers lautet. Das Beispiel aus diesem Abschnitt verwendet hingegen MyRoot für den Tag des Stammelementes des Dokuments. Diese Angabe des Stammelement-Tags ist wichtig, denn die .xslt-Datei verweist auf diese Bezeichnung bei der Umsetzung der XML-Daten in HTML. Geben Sie den Tag für das Stammelement fehlerhaft an, werden Ihre HTMLDaten ebenfalls fehlerhaft sein. Dieser geringfügige Formatierungsunterschied zwischen diesem und dem vorangegangenen Beispiel hilft, Fragen der XSLT-Syntax zu betonen, die das Layout und die Formatierung der Inhalte auf einer HTML-Seite kontrollieren. Sie können das Listing der Datei MyXSLShippers.xslt mit dem vorigen Listing der Datei MyXSL.xslt vergleichen, die verwendet wurde, um das vorige XML-Dokument in eine HTML-Tabelle zu übersetzen. Wie zuvor verfügt die Datei MyXSLShippers.xslt über zwei xsl:template-Elemente. Allerdings entspricht das erste Element aus dieser Datei dem Shippers-Element aus dem Dokument, denn das zugrunde liegende XMLDokument verfügt über drei separate Zeilen, die jeweils mit einem Shippers-Element beginnen. Mit dem Namen dieses Elements (Shippers) können Sie auf die Ansammlung aller Zeilen aus dem XMLDokument verweisen. Beachten Sie außerdem, dass das erste Element xsl:value-of-Elemente umfasst. Die vorige Datei MyXSL.xslt beinhaltete nur zwei dieser Elemente – eines für jede Spalte. Die Formatierungs-Tags, welche die Spaltenwerte umschließen, sind in diesem Beispiel ebenfalls anders. Die ShipperID-Spaltenwerte verfügen beispielsweise über Formatierungen, die eine horizontale Zentrierung bewirken. Darüber hinaus gibt es keine Tags für Fett- oder Kursivschrift. Neben diesen unwesentlichen Unterschieden sind die .xslt-Dateien dieses und des vorigen Beispiels gleich. Die beiden Beispiele ergänzen einander bei der Demonstration allgemeiner XSLT-Codiertechniken zur Umwandlung von XML-Dokumenten in HTML-Tabellen. 502 Kapitel 12 677.book Page 503 Thursday, December 5, 2002 2:26 PM <?xml version='1.0' encoding='UTF-8'?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/ Transform" version="1.0"> <xsl:template match = 'Shippers'> <TR> <TD align = 'middle'><xsl:value-of select = '@ShipperID' /></TD> <TD><xsl:value-of select = '@CompanyName' /></TD> <TD><xsl:value-of select = '@Phone' /></TD> </TR> </xsl:template> <xsl:template match = '/'> <HTML> <HEAD> <STYLE>th { background-color: #CCCCCC }</STYLE> </HEAD> <BODY> <TABLE border='1' style='width:300;'> <TR><TH colspan='3'>Zulieferer</TH></TR> <TR><TH >ShipperID</TH><TH>Firmenname</TH><TH>Telefon</TH></TR> <xsl:apply-templates select = 'MyRoot' /> </TABLE> </BODY> </HTML> </xsl:template> </xsl:stylesheet> Programmieren von XML mit Visual Basic .NET 503 677.book Page 504 Thursday, December 5, 2002 2:26 PM