Das XML-Speicherformat der Änderungsdaten wird als Diffgram bezeichnet. Der Umgang damit erscheint auf den ersten Blick kompliziert, weshalb es offensichtlich erst wenige praktische Anwendungen gibt. Dabei sind die Grundlagen dazu schon einige Jahre alt und das Format selbst ist in seinen Grundzügen eine Norm. Korrekt handelt es sich eigentlich um die XML Diff Language (XDL), der Begriff Diffgram ist lediglich eine Umschreibung, die überwiegend von Microsoft gebraucht wird. Eine W3C-Norm ist Diffgram noch nicht, auch wenn es offensichtlich Überlegungen gibt, XDL entsprechend zu etablieren. Es gibt darüber hinaus mehrere auf XML und proprietären Formaten basierende Differenzsprachen. Es bleibt daher abzuwarten, wie ein endgültiger Standard aussehen wird. Dem aktuellen Einsatz steht jedoch nichts entgegen, denn die Anwendung beschränkt sich auf ein lokales System. Zum Datenaustausch mit Dritten waren Diffgrams nie vorgesehen, sodass spätere Kompatibilitätsprobleme kaum zu befürchten sind.
Ausgangspunkt zur Erzeugung eines Diffgrams sind immer zwei Zustände einer Datenquelle. Das Diffgram selbst enthält Daten, die die Unterschiede repräsentieren. Es ist durch entsprechende Anweisungen möglich, Einfügungen, Änderungen und Löschungen zu speichern.
Ein wenig XML
Innerhalb der .NET-Welt ist die Programmierung von Diffgrams erstaunlich einfach. Es ist jedoch ratsam, sich ein wenig mit den Grundlagen auseinander zu setzen, sei es zur Fehlersuche oder aus rein technischem Interesse. Listing 1 zeigt das Skelett eines Diffgrams.Listing 1
<?xml version="1.0"?><diffgr:diffgramxmlns: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>
Es folgt nach der Festlegung der Datenquelle ein Block innerhalb des Elements <diffgr:before>. Hier sind die Daten aufgeführt, die geändert wurden, also gelöscht oder modifiziert. Die oberste Ebene innerhalb dieses Elements pictureen die Tabellennamen. Es folgt noch - optional - eine Block mit dem Element <diffgr:errors>. Hier werden Fehler aufgezeichnet, die bei der Ermittlung der Datendifferenzen auftraten. Normalerweise sollte dieser Block fehlen.
Die Elemente enthalten mehrere Attribute, mit denen die eigentlichen Änderungen koordiniert werden. Dazu gehört an erster Stelle id, ein Verweis auf das zu ändernde Element der Dateninstanz. Die Art der Änderung wird mit hasChanges festgestellt; es kann die Parameter inserted oder modified enthalten. Zusätzlich kann noch parentID auftreten, durch die das ausführende Programm Kind-Eltern-Beziehungen zwischen Elementen erkennt und die Reihenfolge der Verarbeitung steuert.
Die Logik dahinter
Um Diffgrams zu verstehen, muss man sich mit der Logik zur Feststellung der Änderungen vertraut machen. Das Einfügen eines Datensatzes wird vorgenommen, wenn das Element im Instanz-Block nicht innerhalb der Elemente unterhalb <diff:before> erscheint und das Attribut diffgr:hasChanged=inserted besitzt. Änderungen an den Daten führt der Prozessor aus, wenn das Element im Instanz-Block existiert und das Attribut diffgr:hasChanged=modified vorhanden ist. An dieser Stelle kann ein Fehler auftreten, denn wenn das Element nicht existiert und dennoch geändert werden soll, läuft die Aktion ins Leere. Das passiert, weil Diffgrams selbst nicht die Konsistenz der Daten sicherstellen. Der Instanz-Block ist in der Regel nur ein Verweis auf eine externe Datenquelle, die XML-Stammdaten. Lediglich hinzugefügte Daten erscheinen hier direkt. Das Löschen basiert auf der Auflistung von Elementen innerhalb <diffgr:before>, wobei das Attribut diffgr:hasChanged fehlt. Ein spezielles Attribut gibt es nicht.In der Praxis ist der Umgang natürlich nicht damit verbunden, selbst diese Tags aufzuschreiben oder Programme zu entwickeln, die dies tun. Dank .NET ist der Umgang mit Diffgrams sehr einfach. Eine kleine WinForms-Applikation soll als Ausgangspunkt für die ersten eigenen Versuche dienen.
Die Macht des Benutzers
Gute Software zeichnet sich vor allem durch Benutzerfreundlichkeit aus. Dazu gehört zum einen ein stabiler Betrieb, zum anderen eine gute Anpassbarkeit an Benutzerwünsche. Hat dann der Anwender seine Applikation vollkommen kaputt konfiguriert, sollte ein Klick auf Wiederherstellen oder ein Neustart den Urzustand wieder hervorbringen. Es ist nun naheliegend, hierfür XML und Diffgrams einzusetzen. Die eigentliche Konfiguration wird mit einer XML-Datei vorgenommen, was heute bereits bei vielen Applikationen zum Standard gehört. Oft wird der Anwender oder Administrator jedoch gezwungen sein, Änderungen direkt in dieser XML-Datei vorzunehmen und sich selbst um Sicherheitskopien zu kümmern.Listing 2
<?xml version="1.0" encoding="utf-8" ?><selectboxes xmlns="http://tempuri.org/SelectBoxes.xsd"><box name="Box1"><option d3p1:option="1" xmlns:d3p1="http://tempuri.org/SelectBoxes.xsd">Ihre Auswahl</option><option d3p1:option="2" xmlns:d3p1="http://tempuri.org/SelectBoxes.xsd">Frau</option><option d3p1:option="3" xmlns:d3p1="http://tempuri.org/SelectBoxes.xsd">Herr</option><option d3p1:option="4" xmlns:d3p1="http://tempuri.org/SelectBoxes.xsd">Frau Dr.</option><option d3p1:option="5" xmlns:d3p1="http://tempuri.org/SelectBoxes.xsd">Herr Dr.</option><option d3p1:option="6" xmlns:d3p1="http://tempuri.org/SelectBoxes.xsd">Herr Prof.</option><option d3p1:option="7" xmlns:d3p1="http://tempuri.org/SelectBoxes.xsd">Frau Prof.</option></box></selectboxes>
Nach dieser Vorbereitung geht es nun an die Entwicklung der Testapplikation. Abpictureung 1 zeigt das Ergebnis. Im Code selbst sind drei Steuerelemente von Bedeutung: button1 ist die Schaltfläche zum Entfernen des ausgewählten Eintrags, comboBox1 die Combo-Box zur Anzeige der Liste und zum Eintragen neuer Daten und Label1 dient zur Ausgabe von Statusmeldungen.
Daten aus XML holen
Der Konstruktor der Klasse Form1, die hier verwendet wird, enthält die nötigen Anweisungen zum Lesen der XML-Daten. Benötigt wird dazu ein DataSet:DataSet dsLists = new DataSet();
dsLists.ReadXmlSchema (@\data\SelectBoxes.xsd);dsLists.ReadXml (@\data\SelectBoxes.xml);
dsLists.AcceptChanges();
if (FileExists (@\data\Custom.xml){dsLists.ReadXml(@\data\Custom.xml, XmlReadMode.DiffGram)}
DataView dv = new DataView ();dv.Table = dsLists.Tables["Option"];boxId = dsLists.Tables["box"].Select("name='Box1'")[0]["box_Id"].ToString();dv.RowFilter = "box_Id = " + boxId;comboBox1.DataSource = dv;comboBox1.DisplayMember = "option_Text";comboBox1.ValueMember = "option";
Geben und nehmen
Nach der ersten Initialisierung geht es nun darum, auf die möglichen Benutzeraktionen zu reagieren. Da am Anfang des Programms die Methode AcceptChanges aufgerufen wurde, werden alle Änderungen wie in einem Protokoll automatisch aufgezeichnet. Zwei mögliche Prozesse sind zu verarbeiten: Bei jedem Auslösen der Enter-Taste in der Combobox soll der dann erfasste Text der Liste persönlicher Änderungen hinzugefügt werden. Dazu wird auf das KeyPress-Ereignis reagiert und der Code der Enter-Taste ((char) 13) erkannt:private void comboBox1_Enter(object sender, System.Windows.Forms.KeyPressEventArgs e){if (e.KeyChar == (char) 13){string newEntry = comboBox1.Text;dr = dsLists.Tables["option"].NewRow ();dr["option_Text"] = newEntry;dr["box_Id"] = boxId;dsLists.Tables["option"].Rows.Add (dr);
ds = dsLists.GetChanges ();sw = new StreamWriter(@"..\data\Custom.xml");ds.WriteXml(sw, XmlWriteMode.DiffGram);sw.Close();comboBox1.Refresh ();}}
Kaum aufwändiger ist das Entfernen eines Elements aus der Liste. Die entsprechende Ereignisbehandlungsmethode beginnt mit einer Sicherheitsabfrage, damit keine leeren Einträge gespeichert werden:
private void button1_Click(object sender, System.EventArgs e){if (dsLists.Tables["option"].Rows.Count == 0 || comboBox1.SelectedValue == null){comboBox1.Text = "?";LabelSelection.Text = "Keine Elemente mehr vorhanden";LabelSelection.ForeColor = Color.Red;}else{
dsLists.Tables["option"].Rows.Find(comboBox1.SelectedValue).Delete();
ds = dsLists.GetChanges ();sw = new StreamWriter(@"..\data\Custom.xml");ds.WriteXml(sw, XmlWriteMode.DiffGram);sw.Close();}comboBox1.Refresh ();}
Diffgrams im SQL Server 2000
Ein direkter und einfacher Weg, den SQL Server 2000 einzubeziehen, besteht in der Nutzung der entsprechenden SQL-Klassen und der XML-Unterstützung des DataSets. In der Praxis ist das jedoch möglicherweise nicht ausreichend leistungsfähig. Besser wäre es, wenn die XML-Operationen direkt im SQL Server stattfinden, der Ansatz clientseitiger Datenverarbeitung also vermieden wird. Standardmäßig können mit dem SQL-Provider des Frameworks nur klassische Abfragen verarbeitet werden, jedoch nur eingeschränkt Aufrufe mit der SQLXML-Erweiterung FOR XML. SQLXML 3.0 bringt nicht nur einen verbesserten Zugriff auf die Ergebnisse der FOR XML-Anweisung. Der Zugriff auf relationale Tabellen ist nun per XPath möglich, was manchmal eleganter und einfacher ist. Der spannende Punkt ist aber: Der SQL Server lernt den Umgang mit Diffgrams. Diese sind nicht nur ebenso einfach anwendbar wie in den zuvor gezeigten Beispielen angedeutet, sondern mit denen des .NET-Framework kompatibel. Insofern gibt es außer diesem Umstand wenig Neues zu berichten. Bleibt die Beschaffungsfrage zu klären.Der erste Schritt besteht darin, die SqlXml-Klassen verfügbar zu machen. Dazu ist das entsprechende Paket SQLXML 3.0 SP1 von der Microsoft-MSDN-Website herunterzuladen. Die Installation setzt den SQL Server 2000 bzw. auf einem anderen System die Client-Installation voraus. Erst danach kann die SQLXML-Erweiterung installiert werden.
Nach der Installation liegt die Datei Microsoft.Data.SqlXml.dll im Verzeichnis /Programme/SQLXML 3.0/bin ihres Systemlaufwerks. Wird Visual Studio .NET verwendet, sollte im Projekt eine Referenz zu dieser Datei eingerichtet werden. Kommt dagegen der Kommandozeilen-Compiler zum Einsatz, ist der Schalter /reference:Microsoft.Data.SqlXml.dll beim Übersetzen erforderlich. Außerdem müssen auf dem verwendeten Computer entweder der SQL Server 2000 oder dessen Client-Services installiert sein.
Nach dieser Vorbereitung ist der entsprechende Namensraum verfügbar:
using Microsoft.Data.SqlXml;
In Anbetracht der Fähigkeiten des Frameworks mag es erstaunen, dass SQLXML vergleichbare Klassen zusätzlich liefert. Dies hat zwei Hintergründe. Zum einen kann die Programmierung von Diffgrams auch vom alten ADO und allen anderen COM-Applikationen aus vorgenommen werden. Zum anderen erfolgt die Verarbeitung im SQL Server und damit in der Datenschicht einer Mehrschichtapplikation. In einer verteilten Umgebung kann dies die Clients signifikant entlasten. Ob das praktikabel ist, hängt von der Anwendung ab. Während .NET hier keine Wahl lässt, erweitert der Einsatz des SQL Servers und seiner XML-Fähigkeiten die Lösungswege.
Und noch eine Funktion erweitert das Einsatzspektrum der Diffgrams. Mit dem SQLXML 3.0-Pack wird eine ISAPI-Erweiterung installiert, die das Ausführen von Diffgrams direkt im Browser erlaubt. Gestartet wird eine XML-Datei, die - wenn sie als Diffgram erkannt wird - mit den entsprechenden Parametern an den SQL Server geleitet wird. Eine optionale Transformation produziert parallel dazu das Ergebnis für den Betrachter.
Einen Schritt weiter gehen die SOAP-Erweiterungen im Paket, die diese Technik mit Web Services kombinieren und damit den Abgleich von Datenbanken über reine Web-Techniken in besonders effizienter Weise erlauben.












