34.2 Verwenden des DataSet-Objekts
34.2.1 Ein DataSet-Objekt erzeugen

Die Klasse DataSet befindet sich, wie viele andere Klassen auch, die nicht providerspezifisch sind, im Namespace System.Data. In den meisten Fällen ist der parameterlose Konstruktor vollkommen ausreichend, um ein DataSet-Objekt zu erzeugen.
DataSet ds = new DataSet();
Soll das DataSet einen Namen erhalten, bietet sich alternativ der einfach parametrisierte Konstruktor an:
DataSet ds = new DataSet("Bestellungen");
Der Name kann auch über die Eigenschaft DataSetName festgelegt oder abgerufen werden.
34.2.2 Die Anatomie einer DataTable

Zum Leben erweckt wird ein DataSet-Objekt nicht durch die Instanziierung der Klasse, sondern vielmehr durch den Aufruf der Fill-Methode des DataAdapters.
[...]
string strSQL = "SELECT * FROM Products";
SqlDataAdapter da = new SqlDataAdapter(strSQL, con);
DataSet ds = new DataSet();
da.Fill(ds);
[...]
Das Ergebnis der Abfrage enthält alle Datensätze der Tabelle Products. Die Datensätze sind in einer Tabelle enthalten, die durch ein DataTable-Objekt beschrieben wird. Ein DataTable-Objekt beschreibt die Spalten, die im SELECT-Statement der Abfrage angegeben sind. Jede Spalte wird dabei als Objekt vom Typ DataColumn behandelt. Um eine einfache Verwaltung und einen einfachen Zugriff auf bestimmte Spalten zu gewährleisten, werden alle Spalten in eine Auflistung der DataTable eingetragen. Über die Eigenschaft Columns der DataTable erhalten Sie Zugriff auf die DataColumnCollection.
In ähnlicher Weise ist auch das Ergebnis der Abfrage organisiert. Jeder zurückgelieferte Datensatz wird durch ein Objekt vom Typ DataRow beschrieben. Alle Datenzeilen in einer Tabelle werden von einer Auflistung verwaltet, der DataRowCollection, auf die Sie über die DataTable-Eigenschaft Rows zugreifen können.
Eine DataTable hat eine DataColumn- und eine DataRowCollection. Da ein DataSet nicht nur eine, sondern prinzipiell beliebig viele Tabellen enthalten kann, muss auch der Zugriff auf eine bestimmte DataTable im DataSet möglich sein. Wie kaum anders zu erwarten ist, werden auch alle Tabellen in einem DataSet von einer Auflistung organisiert. Diese ist vom Typ DataTableCollection, deren Referenz die Eigenschaft Tables des DataSets liefert.
Abbildung 34.1 Die Struktur eines DataSets
34.2.3 Der Zugriff auf eine Tabelle im DataSet

Wenn ds ein DataSet-Objekt beschreibt, genügt eine Anweisung wie die folgende, um auf eine bestimmte Tabelle im DataSet zuzugreifen:
ds.Tables[2]
Enthält das DataSet mehrere Tabellen, lassen sich die Indizes oft nur schwer einer der Tabellen zuordnen. Wie Sie wissen, weist der SqlDataAdapter per Vorgabe den Tabellen im DataSet ebenfalls Bezeichner (Table, Table1, Table2 usw.) zu. Sowohl die Indizes als auch die Standardbezeichner sind aber wenig geeignet, um den Code gut lesbar zu gestalten. Der SqlDataAdapter unterstützt eine DataTableMappingCollection, um lesbare Tabellennamen abzubilden. Zudem bietet die Überladung der Methode des SqlDataAdapters die Möglichkeit, einer Tabelle einen sprechenden Bezeichner zuzuordnen. Sie sollten beherzigen, eines dieser Angebote zu nutzen, denn die Anweisung
ds.Tables["Artikel"]
wird Ihnen später eher hilfreich sein, den eigenen Programmcode zu verstehen, als die Angabe eines nur schlecht zuzuordnenden Index.
34.2.4 Der Zugriff auf die Ergebnisliste

Ein DataRow-Objekt stellt den Inhalt eines Datensatzes dar und kann sowohl gelesen als auch geändert werden. Um in einer DataTable von einem Datensatz zum anderen zu navigieren, benutzen Sie die Eigenschaft Rows der DataTable, die die Referenz auf das DataRowCollection-Objekt der Tabelle zurückgibt und alle Datensätze enthält, die Ergebnis der Abfrage sind. Die einzelnen DataRows sind über den Index der Auflistung adressierbar.
Mit der folgenden Anweisung wird der Verweis auf die dritte Datenzeile in der ersten Tabelle des DataSets der Variablen row zugewiesen.
DataRow row = ds.Tables["Artikel"].Rows[2];
Eine Datenzeile nur zu referenzieren, ist sicher nicht das von Ihnen verfolgte Ziel. Vielmehr werden Sie daran interessiert sein, den Inhalt einer oder mehrerer Spalten der betreffenden Datenzeile auszuwerten. Dazu veröffentlicht die DataRow einen Indexer, dem Sie entweder den Namen der Spalte, deren Index in der DataColumnCollection der DataTable (die Ordinalposition) oder die Referenz auf die gewünschte Spalte übergeben. Der Rückgabewert ist jeweils vom Typ Object und enthält die Daten der angegebenen Spalte. Häufig ist eine anschließende Konvertierung in den richtigen Datentyp notwendig. Setzen Sie die Überladung ein, die den Spaltenbezeichner erwartet, müssen zwei Ausgangssituationen beachtet werden: Per Vorgabe setzen Sie diejenigen Spaltenbezeichner ein, die auch in der Originaldatenbank bekannt sind. Haben Sie jedoch der DataColumnMappingCollection Spaltenzuordnungen hinzugefügt, müssen Sie diese angeben.
Wir wollen uns dies nun in einem Beispiel ansehen.
// Beispiel: ..\Kapitel 34\ShowDataRows
class Program {
static void Main(string[] args) {
SqlConnection con = new SqlConnection();
con.ConnectionString = "...";
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
cmd.CommandText = "SELECT ProductName, UnitPrice " +
"FROM Products " +
"WHERE UnitsOnOrder > 0";
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds, "Artikel");
// Ausgabe der Ergebnisliste
DataTable tbl = ds.Tables["Artikel"];
for (int i = 0; i < tbl.Rows.Count; i++) {
Console.WriteLine("{0,-35}{1}",
tbl.Rows[i]["ProductName"], tbl.Rows[i]["UnitPrice"]);
}
Console.ReadLine();
}
}
Listing 34.1 Ausgabe des Inhalts eines DataSets
Gefragt wird nach allen Artikeln, zu denen aktuell Bestellungen vorliegen. Nach dem Füllen des DataSets wird die Ergebnisliste in einer Schleife durchlaufen. Der Schleifenzähler wird dabei als Index der Datenzeile eingetragen. Damit die einzelnen Anweisungen nicht zu lang werden, wird vor Beginn des Schleifendurchlaufs die DataTable im DataSet in einer Variablen gespeichert.
DataTable tbl = ds.Tables["Artikel"];
Da alle Datenzeilen von einer Auflistung verwaltet werden, stehen die üblichen Methoden und Eigenschaften zur Verfügung. In diesem Code wird die Eigenschaft Count abgefragt, um festzustellen, wie viele Datenzeilen sich in der Ergebnisliste befinden.
Sie können auch statt der for-Schleife eine foreach-Schleife einsetzen. Der folgende Codeausschnitt ersetzt daher vollständig die for-Schleife unseres Beispiels.
foreach(DataRow row in tbl.Rows)
Console.WriteLine("{0,-35}{1}", row["ProductName"], row["UnitPrice"]);
34.2.5 Dateninformationen in eine XML-Datei schreiben
Sie können die Dateninformationen eines DataSets in eine XML-Datei schreiben und später im Bedarfsfall auch wieder laden. Hierzu stehen Ihnen mit WriteXml und ReadXml die passenden Methoden zur Verfügung, die auf die Referenz des DataSet-Objekts aufgerufen werden. Beiden Methoden können Sie als Parameter den Namen der Datei angeben, in die die Daten gespeichert bzw. aus der die XML-Daten gelesen werden sollen:
ds.WriteXml(@"D:\Daten\ContentsOfDataset.xml");
[...]
ds.ReadXml(@"D:\Daten\ContentsOfDataset.xml");
Listing 34.2 Daten im XML-Format speichern und laden
Der Parameter beschränkt sich nicht nur auf Dateien. Sie können auch einen TextReader, einen Stream oder einen XmlReader angeben.
Nachfolgend sehen Sie den Teilausschnitt eines XML-Dokuments, dem die Abfrage
SELECT ProductID, ProductName FROM Products
zugrunde liegt.
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<Table>
<ProductID>1</ProductID>
<ProductName />
</Table>
<Table>
<ProductID>17</ProductID>
<ProductName>Alice Mutton</ProductName>
</Table>
<Table>
<ProductID>3</ProductID>
<ProductName>Aniseed Syrup</ProductName>
</Table>
<Table>
<ProductID>40</ProductID>
<ProductName>Boston Crab Meat</ProductName>
</Table>
<Table>
<ProductID>60</ProductID>
<ProductName>Camembert Pierrot</ProductName>
</Table>
...
</NewDataSet>
Listing 34.3 Daten einer DataTable im XML-Format
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.