Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Über den Autor
Vorwort zur 4. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign und Vererbung
4 Weitere .NET-Datentypen
5 Weitere Möglichkeiten von C#
6 Projektmanagement und Visual Studio 2008
7 Fehlerbehandlung und Debugging
8 LINQ
9 Multithreading und asynchrone Methodenaufrufe
10 Arbeiten mit Dateien und Streams
11 Serialisierung
12 Einige wichtige .NET-Klassen
13 Grundlagen zum Erstellen einer Windows-Anwendung
14 Die wichtigsten Steuerelemente
15 Tastatur- und Mausereignisse
16 MDI-Anwendungen
17 Grafische Programmierung mit GDI+
18 Das Drucken (Printing)
19 Steuerelemente entwickeln
20 Programmiertechniken
21 WPF – die Grundlagen
22 Die Layoutcontainer
23 Die WPF-Controls
24 Konzepte von WPF
25 ADO.NET – die Verbindung zu einer Datenbank herstellen
26 Die Datenbankabfrage
27 Der SqlDataAdapter
28 Daten im lokalen Speicher – das DataSet
29 Eine Datenbank aktualisieren
30 Stark typisierte DataSets
31 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2008 von Andreas Kuehnel
Das umfassende Handbuch
Buch: Visual C# 2008

Visual C# 2008
geb., mit DVD
1.366 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1172-7
Pfeil 28 Daten im lokalen Speicher – das DataSet
Pfeil 28.1 Verwenden des »DataSet«-Objekts
Pfeil 28.1.1 Ein »DataSet«-Objekt erzeugen
Pfeil 28.1.2 Die Anatomie einer »DataTable«
Pfeil 28.1.3 Der Zugriff auf eine Tabelle im »DataSet«
Pfeil 28.1.4 Der Zugriff auf die Ergebnisliste
Pfeil 28.1.5 Dateninformationen in eine XML-Datei schreiben
Pfeil 28.2 Dem »DataSet« Schemainformationen übergeben
Pfeil 28.2.1 Schemainformationen bereitstellen
Pfeil 28.2.2 Eigenschaften einer »DataColumn«, die der Gültigkeitsprüfung dienen
Pfeil 28.2.3 Die »Constraints«-Klassen einer »DataTable«
Pfeil 28.2.4 Das Schema mit Programmcode erzeugen
Pfeil 28.2.5 Schemainformationen mit dem »SqlDataAdapter« abrufen
Pfeil 28.2.6 Schemainformationen aus einer XML-Schemadatei beziehen
Pfeil 28.3 Änderungen in einer »DataTable« vornehmen
Pfeil 28.3.1 Editieren einer »DataRow«
Pfeil 28.3.2 Löschen einer Datenzeile
Pfeil 28.3.3 Eine neue Datenzeile hinzufügen
Pfeil 28.4 Was bei der Änderung einer Datenzeile passiert
Pfeil 28.4.1 Die Eigenschaft »RowState«
Pfeil 28.4.2 Der ursprüngliche und der aktualisierte Inhalt einer Datenzeile
Pfeil 28.4.3 Die Eigenschaft »DataRowState« manuell steuern
Pfeil 28.5 Mit mehreren Tabellen arbeiten
Pfeil 28.5.1 Der Weg über JOIN-Abfragen
Pfeil 28.5.2 Mehrere Tabellen in einem DataSet
Pfeil 28.5.3 Eine »DataRelation« erzeugen
Pfeil 28.5.4 »DataRelations« und Einschränkungen
Pfeil 28.5.5 In Beziehung stehende Daten suchen
Pfeil 28.5.6 Ergänzung zum Speichern von Schemainformationen in einer XML-Schemadatei
Pfeil 28.6 Suchen und filtern im »DataSet«
Pfeil 28.6.1 Die »Find«-Methode
Pfeil 28.6.2 Die »Select«-Methode
Pfeil 28.7 Objekte vom Typ »DataView«
Pfeil 28.7.1 Eine »DataView« erzeugen
Pfeil 28.7.2 Auf die Datenzeilen in einer »DataView« zugreifen
Pfeil 28.7.3 Die Eigenschaft »Sort« und die Methode »Find«
Pfeil 28.7.4 Die Methode »FindRows«
Pfeil 28.7.5 Die Eigenschaft »RowFilter«
Pfeil 28.7.6 Die Eigenschaft »RowStateFilter«
Pfeil 28.7.7 Änderungen an einem »DataView«-Objekt
Pfeil 28.7.8 Aus einer »DataView« eine »DataTable« erzeugen


Galileo Computing - Zum Seitenanfang

28.5 Mit mehreren Tabellen arbeiten Zur nächsten ÜberschriftZur vorigen Überschrift


Galileo Computing - Zum Seitenanfang

28.5.1 Der Weg über JOIN-Abfragen Zur nächsten ÜberschriftZur vorigen Überschrift

Bisher haben wir immer nur eine Tabelle im DataSet betrachtet. Das entspricht aber nur in wenigen Fällen den üblichen Anforderungen in der Praxis. Um beispielsweise die Frage zu beantworten, welche Artikel von den einzelnen Lieferanten stammen, sind zwei Tabellen notwendig: Products und Suppliers. Die meisten Tabellen einer Datenbank stehen mit anderen Tabellen in Beziehung. Meistens handelt es sich dabei um 1:n-Beziehungen. Beispielsweise stammen von einem Lieferanten mehrere Artikel. Allerdings berücksichtigt die Northwind-Datenbank nicht, dass ein bestimmtes Produkt durchaus auch von mehreren Lieferanten angeboten werden könnte. Dann müsste die Beziehung zwischen den beiden Tabellen durch eine m:n-Beziehung beschrieben werden, die normalerweise in drei Tabellen aufgelöst wird, die miteinander jeweils in einer 1:n-Beziehung stehen.

Wenden wir uns für die weiteren Ausführungen nun den beiden Tabellen Products und Suppliers zu, deren Beziehung Sie in Abbildung 28.3 sehen.

Abbildung 28.3 Die Beziehung zwischen den Tabellen »Products« und »Suppliers«

Um Daten aus mehreren Tabellen auszuwerten, werden üblicherweise JOIN-Abfragen benutzt. Wollen Sie zum Beispiel wissen, welche Produkte von den einzelnen Lieferanten angeboten werden, könnte die Abfrage wie folgt lauten:

SELECT Suppliers.CompanyName, Suppliers.ContactName, 
       Products.ProductName, Products.UnitPrice 
FROM Suppliers INNER JOIN 
     Products ON Suppliers.SupplierID = Products.SupplierID

Das Ergebnis der Abfrage sehen Sie in Abbildung 28.4.

Abbildung 28.4 Ergebnisliste einer JOIN-Abfrage

JOIN-Abfragen haben einige Vorteile:

  • Das Ergebnis lässt sich filtern.
  • Das Resultat steht in einer überschaubaren Ergebnismenge.
  • JOIN-Abfragen sind anerkannter Standard.

Bei kritischer Betrachtung stehen den Vorteilen aber auch schwerwiegende Nachteile gegenüber:

  • Die Daten einer JOIN-Abfrage sind schwierig zu aktualisieren. Insbesondere beim Löschen oder Hinzufügen einer Datenzeile in einer JOIN-Abfrage wird die Problematik deutlich. Angenommen, Sie löschen eine Datenzeile, steht sofort die Frage im Raum, ob nur die Datenzeile in der Detailtabelle, also auf der n-Seite einer Beziehung gelöscht werden soll oder gleichzeitig auch die Datenzeile in der übergeordneten Mastertabelle, also der 1-Seite.
  • JOIN-Abfragen geben redundante Daten zurück. Lassen Sie sich beispielsweise die Artikelliste ausgeben und zu jedem Artikel auch noch die notwendigen Informationen des entsprechenden Lieferanten, werden die Lieferanteninformationen mehrfach zurückgeliefert (siehe dazu auch Abbildung 28.4).
  • Änderungen in einer JOIN-Abfrage sind schwer zu synchronisieren. Firmiert sich einer der Lieferanten um und tragen Sie das im Abfrageergebnis ein, muss die Änderung sofort zur Datenbank übermittelt und die gesamte Abfrage erneut ausgeführt werden.

Galileo Computing - Zum Seitenanfang

28.5.2 Mehrere Tabellen in einem DataSet Zur nächsten ÜberschriftZur vorigen Überschrift

ADO.NET löst die Nachteile, die eine JOIN-Abfrage hat, auf eigene Art und Weise. Dazu wird die JOIN-Abfrage in Einzeltabellen aufgeteilt, die miteinander in Beziehung gesetzt werden. Mit anderen Worten: Es wird ein Teil der Originaldatenbank abgebildet. Die Beziehung zwischen zwei Tabellen wird durch ein Objekt vom Typ DataRelation beschrieben.

Obschon solchermaßen strukturierte DataSets schwer zu filtern sind, überwiegen die Vorteile. So werden weniger Daten zurückgegeben als bei einer JOIN-Abfrage. Damit wird einerseits sowohl die Netzbelastung als auch die Auslastung des lokalen Speichers so gering wie möglich gehalten. Zudem ist es viel einfacher, Daten zu aktualisieren. Löschen Sie zum Beispiel einen Datensatz aus der Detailtabelle (n-Seite), möchten Sie vermutlich nicht auch gleichzeitig den entsprechenden Datensatz der Mastertabelle löschen (1-Seite). Beide Informationen sind in einer JOIN-Abfrage jedoch in einer Datenzeile zusammengefasst. Operieren Sie mit einer DataRelation zwischen zwei DataTable-Objekten, lässt sich der Datensatz aus der Detailtabelle löschen, ohne dass zwangsläufig auch die entsprechende Datenzeile der Mastertabelle gelöscht wird.


Galileo Computing - Zum Seitenanfang

28.5.3 Eine »DataRelation« erzeugen Zur nächsten ÜberschriftZur vorigen Überschrift

Mithilfe einer DataRelation werden zwei DataTable-Objekte über DataColumn-Objekte miteinander verknüpft. In der Products/Suppliers-Beziehung ist die Tabelle Suppliers das übergeordnete Element und die Tabelle Products das untergeordnete Element der Beziehung. Dies ist vergleichbar mit einer Primärschlüssel/Fremdschlüssel-Beziehung. Beziehungen werden zwischen einander entsprechenden Spalten in der übergeordneten und der untergeordneten Tabelle erstellt. Das heißt, dass der Datentyp für beide Spalten identisch sein muss.

Ich möchte Ihnen im Folgenden einen der vielen DataRelation-Konstruktoren vorstellen:

public DataRelation(string relationName, DataColumn parentColumn, DataColumn childColumn);

Dem ersten Parameter teilen Sie mit, unter welchem Namen die DataRelation angesprochen werden soll, der zweite Parameter erwartet die Referenz auf die übergeordnete Spalte der Mastertabelle (1-Seite) und der dritte Parameter die Referenz auf die untergeordnete Spalte der Detailtabelle (n-Seite).

Nachdem eine DataRelation erzeugt worden ist, muss sie dem DataSet bekannt gegeben werden. Dazu enthält das DataSet eine Auflistung vom Typ DataRelationCollection. Die Eigenschaft Relations des DataSets gibt die Referenz auf die Auflistung zurück.

Das folgende Beispiel zeigt, wie die durch ein Programm erzeugte Beziehung zwischen den beiden Tabellen Suppliers und Products festgelegt wird:

SqlConnection con = new SqlConnection(); 
con.ConnectionString = "..."; 
SqlCommand cmd = new SqlCommand(); 
cmd.Connection = con; 
cmd.CommandText = "SELECT * FROM Suppliers; " + 
                  "SELECT * FROM Products"; 
DataSet ds = new DataSet(); 
SqlDataAdapter da = new SqlDataAdapter(cmd); 
da.TableMappings.Add("Table", "Lieferanten"); 
da.TableMappings.Add("Table1", "Produkte"); 
da.Fill(ds); 
// Erzeugen der Beziehung zwischen den beiden Tabellen 
DataColumn colMaster = ds.Tables["Lieferanten"].Columns["SupplierID"]; 
DataColumn colDetail = ds.Tables["Produkte"].Columns["SupplierID"]; 
DataRelation rel = new DataRelation("LieferantenProdukte", 
                                    colMaster, colDetail); 
ds.Relations.Add(rel);

Galileo Computing - Zum Seitenanfang

28.5.4 »DataRelations« und Einschränkungen Zur nächsten ÜberschriftZur vorigen Überschrift

Wenn Sie eine DataRelation zwischen zwei Tabellen erzeugen, wie zuvor gezeigt, wird ein UniqueConstraint auf der Mastertabelle und ein ForeignKeyConstraint auf der Detailtabelle erstellt.

Haben Sie vor dem Erstellen der DataRelation Einschränkungen definiert, die einer Unique- und einer Fremdschlüsseleinschränkung entsprechen, übernimmt die neue DataRelation die vorhandenen Einschränkungen und erzeugt implizit keine neuen.

Andererseits können Sie allerdings auch das implizite Erzeugen der Einschränkungen unterdrücken. Dazu übergeben Sie dem Konstruktor der DataRelation im vierten Parameter false, z. B.:

DataRelation rel = new DataRelation("LieferantenProdukte", 
                    colMaster, colDetail, false);

Das »ForeignKeyConstraint«-Objekt im Detail

Das ForeignKeyConstraint-Objekt gehört zur ConstraintCollection der Detailtabelle, also der Tabelle auf der n-Seite einer 1:n-Beziehung. Es weist nicht nur eine Reihe von Eigenschaften auf, um die Beziehung zwischen den beiden Tabellen zu untersuchen, sondern gestattet darüber hinaus, festzulegen, wie sich die beiden Tabellen verhalten, wenn in der übergeordneten Mastertabelle Daten geändert oder Datenzeilen gelöscht werden.

Das folgende Codefragment zeigt, wie Sie die Eigenschaften Table, RelatedTable, Columns und RelatedColumns auswerten können. Die Auswertung basiert auf der Beziehung, die weiter oben zwischen den beiden Tabellen Suppliers und Products codiert worden ist.

ConstraintCollection constr = ds.Tables["Produkte"].Constraints; 
foreach (Constraint cTemp in constr) { 
  if (cTemp is ForeignKeyConstraint) { 
    Console.Write("Untergeordnete Tabelle: "); 
    Console.WriteLine(((ForeignKeyConstraint)cTemp).Table); 
    Console.Write("Untergeordnete Spalte(n): "); 
    foreach (DataColumn col in ((ForeignKeyConstraint)cTemp).Columns) 
      Console.WriteLine(col.ColumnName); 
      Console.Write("Übergeordnete Tabelle: "); 
      Console.WriteLine(((ForeignKeyConstraint)cTemp).RelatedTable); 
      Console.Write("Übergeordnete Spalte(n): "); 
      foreach (DataColumn col in ((ForeignKeyConstraint)cTemp).RelatedColumns) 
        Console.WriteLine(col.ColumnName); 
  } 
}

Table liefert die Referenz auf die untergeordnete Tabelle, RelatedTable die auf die übergeordnete (Mastertabelle). Columns beschreibt die Spalten der untergeordneten Tabelle der Einschränkung, RelatedColumns die der übergeordneten Tabelle. Beide zuletzt genannten Eigenschaften liefern ein DataColumn-Array zurück, weil mehrere Spalten ein gemeinsames Merkmal für die Beziehung zwischen zwei Tabellen darstellen können.

Wichtiger als die Auswertung der Eigenschaften einer Beziehung sind die Eigenschaften, über die das Verhalten der Relation festgelegt wird. Wenn Sie beispielsweise eine Datenzeile in der Mastertabelle löschen, stellt sich die Frage, wie sich die verknüpften Datenzeilen in der untergeordneten Detailtabelle verhalten sollen. Sollen sie ebenfalls gelöscht werden? Oder sollen sie in der Detailtabelle erhalten bleiben? Was ist, wenn in der Mastertabelle ein Wert geändert wird? Wird dann der Wert in der untergeordneten Tabelle ebenfalls aktualisiert?

Die Steuerung dieses Verhaltens wird von den Eigenschaften UpdateRule und DeleteRule bestimmt. Beide Eigenschaften sind vom Typ Rule. Dabei handelt es sich um eine Enumeration im Namespace System.Data, deren Werte in Tabelle 28.7 angegeben sind.


Tabelle 28.7 Die Werte der Enumeration »Rule«

Wert Beschreibung

Cascade

Hierbei handelt es sich um die Standardeinstellung. Wird eine Datenzeile in der Mastertabelle gelöscht (geändert), werden auch alle Detaildatenzeilen gelöscht (geändert).

None

Es wird keine Aktion ausgeführt und stattdessen eine Ausnahme ausgelöst.

SetDefault

Die Werte in den verknüpften Datenzeilen der Detailtabelle werden auf Standardwerte eingestellt. Die Standardwerte werden über die Eigenschaft DefaultValue des DataColumn-Objekts festgelegt.

SetNull

Die Werte in den verknüpften Datenzeilen werden auf DBNull festgelegt.


Eine dritte Eigenschaft, die in diesem Zusammenhang auch noch erwähnt werden sollte, ist AcceptRejectRule. Diese gibt an, wie mit den verknüpften Datenzeilen umgegangen wird, wenn in der Mastertabelle AcceptChanges oder RejectChanges aufgerufen wird.

Die Eigenschaft kann nur zwei Werte annehmen: entweder AcceptRejectRule.None oder AcceptRejectRule.Cascade. Der Vorgabewert ist None. Das bedeutet, dass der Aufruf von AcceptChanges oder RejectChanges auf einer Datenzeile sich nicht auf die untergeordneten Datenzeilen auswirkt. Wenn Sie die Eigenschaft AcceptRejectRule auf Cascade setzen, wird die Aktion an die untergeordnete Datenzeile weitergegeben, die vom ForeignKeyConstraint-Objekt definiert ist.

Das folgende Beispielprogramm DataRelationDemo enthält zusammenfassend einige der Features, die im Zusammenhang mit der DataRelation stehen.

// ---------------------------------------------------
// Beispiel: ...\Kapitel 28\DataRelationDemo
// -------------------------------------------------------
class Program {

  static void Main(string[] args) { 
    SqlConnection con = new SqlConnection(); 
    con.ConnectionString = "..."; 
    SqlCommand cmd = new SqlCommand(); 
    cmd.Connection = con; 
    cmd.CommandText = "SELECT * FROM Suppliers; " + 
                      "SELECT * FROM Products"; 
    DataSet ds = new DataSet(); 
    SqlDataAdapter da = new SqlDataAdapter(cmd); 
    da.TableMappings.Add("Table", "Lieferanten"); 
    da.TableMappings.Add("Table1", "Produkte"); 
    da.Fill(ds);

    // Erzeugen der Beziehung zwischen den beiden Tabellen 
    DataColumn colMaster = ds.Tables["Lieferanten"].Columns["SupplierID"]; 
    DataColumn colDetail = ds.Tables["Produkte"].Columns["SupplierID"]; 
    DataRelation rel = 
        new DataRelation("LieferantenProdukte", colMaster, colDetail); 
    ds.Relations.Add(rel);

    // Untersuchung des ForeignKeyConstraint-Objekts 
    ConstraintCollection constr = ds.Tables["Produkte"].Constraints; 
    foreach (Constraint cTemp in constr) { 
      if (cTemp is ForeignKeyConstraint) { 
        Console.Write("Untergeordnete Tabelle: "); 
       Console.WriteLine(((ForeignKeyConstraint)cTemp).Table); 
        Console.Write("Untergeordnete Spalte(n): "); 
        foreach (DataColumn col in ((ForeignKeyConstraint)cTemp).Columns) 
          Console.WriteLine(col.ColumnName); 
        Console.Write("Übergeordnete Tabelle: "); 
        Console.WriteLine(((ForeignKeyConstraint)cTemp) 
                                             .RelatedTable); 
        Console.Write("Übergeordnete Spalte(n): "); 
        foreach (DataColumn col in ((ForeignKeyConstraint)cTemp)._ 
                                                              RelatedColumns) 
          Console.WriteLine(col.ColumnName);

      } 
    }

    // Datenzeilenliste vor dem Löschen 
    ListData(ds);

    // Löschen einiger Lieferanten 
    ForeignKeyConstraint fkey = 
     (ForeignKeyConstraint)ds.Tables["Produkte"].Constraints[0]; 
    fkey.DeleteRule = Rule.SetNull; 
    for (int i = 0; i < 3; i++) 
      ds.Tables["Lieferanten"].Rows[i].Delete();

    // Ausgabe der Produktliste nach dem Löschen 
    ListData(ds); 
    Console.ReadLine(); 
  }

  static void ListData(DataSet ds) { 
    Console.WriteLine(new string('-', 40)); 
    int counter = 0; 
    foreach (DataRow row in ds.Tables["Produkte"].Rows) 
      if (row.RowState != DataRowState.Deleted) { 
        Console.WriteLine(row["ProductName"]); 
        counter++; 
      } 
    Console.WriteLine("Anzahl der Datenzeilen: {0}", counter); 
    Console.WriteLine(new string('-', 40)); 
  } 
}

Galileo Computing - Zum Seitenanfang

28.5.5 In Beziehung stehende Daten suchen Zur nächsten ÜberschriftZur vorigen Überschrift

DataRelation-Objekte werden oft dazu benutzt, um Daten zu suchen, die in verschiedenen DataTable-Objekten enthalten sind. Zu diesem Zweck stellt eine DataRow drei Methoden zur Verfügung, die auf einer DataRelation basieren:

  • GetChildRows
  • GetParentRow
  • GetParentRows

GetChildRows sucht, ausgehend von einer Datenzeile in der Mastertabelle, alle zugehörigen untergeordneten Datenzeilen in der Detailtabelle. Dazu übergeben Sie der Methode die DataRelation, die beide Tabellen miteinander verknüpft, und erhalten als Ergebnis ein DataRow-Array.

DataRow[] GetChildRows(DataRelation);

Ausgehend von der untergeordneten Zeile einer Detailtabelle ruft GetParentRow die zugehörige übergeordnete Datenzeile aus einer Mastertabelle ab. Auch dieser Methode müssen Sie die DataRelation zwischen den beiden Tabellen angeben; der Rückgabewert ist eine einzige Datenzeile.

DataRow GetParentRow(DataRelation);

Sollte zwischen zwei Tabellen eine n:m-Beziehung bestehen, können Sie die GetParentRows-Methode einsetzen.

DataRow[] GetParentRows(DataRelation);

Ich möchte Ihnen nun in einem Beispielprogramm die Benutzung der Methoden zeigen. Die Aufgabenstellung dazu lautet, dass zu den einzelnen Aufträgen (Tabelle Orders) die bestellten Produkte (Tabelle Products) aufgelistet werden sollen. Zwischen diesen beiden Tabellen besteht eine m:n-Beziehung, die durch die Tabelle Order Details in zwei 1:n-Beziehungen aufgelöst wird (siehe Abbildung 28.5).

Abbildung 28.5 Die Beziehungen zwischen den Tabellen »Orders«, »Order Details« und »Products«

// --------------------------------------------------------------
// Beispiel: ...\Kapitel 28\DataRelationNavigation
// ------------------------------------------------------------------
Class Program {

static void Main(string[] args) { 
  SqlConnection con = new SqlConnection(); 
  con.ConnectionString = "..."; 
  SqlCommand cmd = new SqlCommand(); 
  cmd.Connection = con; 
  cmd.CommandText = "SELECT * FROM Orders; " + 
                    "SELECT * FROM [Order Details]; " + 
                    "SELECT * FROM Products"; 
  DataSet ds = new DataSet(); 
  SqlDataAdapter da = new SqlDataAdapter(cmd); 
  da.TableMappings.Add("Table", "Bestellungen"); 
  da.TableMappings.Add("Table1", "Bestelldetails"); 
  da.TableMappings.Add("Table2", "Produkte"); 
  da.Fill(ds);

  // DataRelation zwischen 'Orders' und 
  // 'Order Details' erzeugen 
  DataColumn colMaster = ds.Tables["Bestellungen"].Columns["OrderID"]; 
  DataColumn colDetail = ds.Tables["Bestelldetails"].Columns["OrderID"]; 
  DataRelation rel = 
          new DataRelation("Bestellungen_Bestelldetails", 
          colMaster, colDetail); 
  ds.Relations.Add(rel);

  // DataRelation zwischen 'Order Details' und 
  // 'Products' erzeugen 
  colMaster = ds.Tables["Produkte"].Columns["ProductID"]; 
  colDetail = ds.Tables["Bestelldetails"].Columns["ProductID"]; 
  rel = new DataRelation("Produkte_Bestelldetails", colMaster, colDetail); 
  ds.Relations.Add(rel);

  // Zu jeder Bestellung die bestellten Artikel anzeigen 
  foreach (DataRow rowOrder in ds.Tables["Bestellungen"].Rows) 
  { 
    Console.WriteLine("BestellNummer: {0}", rowOrder["OrderID"]); 
    foreach (DataRow rowOrderDetail in rowOrder.GetChildRows( 
               ds.Relations["Bestellungen_Bestelldetails"])) 
    { 
      DataRow rowProduct; 
      rowProduct = rowOrderDetail.GetParentRow( 
               ds.Relations["Produkte_Bestelldetails"]); 
      Console.WriteLine("Artikel: {0}", rowProduct["ProductName"]); 
    } 
    Console.WriteLine(new string('-', 40)); 
  } 
  Console.ReadLine(); 
}

}

In diesem Beispiel werden vor dem Füllen des DataSets den drei beteiligten Tabellen zunächst über TableMappings sprechende Bezeichner zugeteilt. Hier ist ein solches Vorgehen besonders empfehlenswert, um den Code besser lesbar zu gestalten. Nachdem der SqlDataAdapter die Daten abgerufen hat, werden in einer äußeren foreach-Schleife alle Datenzeilen der Tabelle Orders nacheinander durchlaufen. Auf jede Datenzeile (also Bestellnummer) wird GetChildRows aufgerufen. Diese Methode liefert ein DataRow-Array zurück, das alle Datenzeilen aus Order Details enthält, die dieser Bestellnummer entsprechen. Aus der gefundenen Datenzeile wird anschließend das Feld ProductID extrahiert, und mit GetParentRow wird die entsprechende Datenzeile in der Tabelle Products gesucht. Letztere liefert uns den Artikelnamen.


Galileo Computing - Zum Seitenanfang

28.5.6 Ergänzung zum Speichern von Schemainformationen in einer XML-Schemadatei topZur vorigen Überschrift

In Abschnitt 28.2.6 habe ich Ihnen gezeigt, wie Sie die Schemainformationen mit der Methode WriteXmlSchema des DataSets in einer XML-Schemadatei speichern können. Ich hatte Ihnen das auch an einem Beispiel demonstriert. Sie können selbstverständlich auf die gleiche Weise auch die Metadaten mehrerer Tabellen, die sich in einem DataSet befinden, in einer Datei bereitstellen. Allerdings gibt es dabei einen besonderen Punkt zu beachten: Wenn Sie das DataSet durch den Aufruf der Methode FillSchema mit Metadaten füllen, werden die Beziehungen zwischen den Tabellen nicht berücksichtigt, auch wenn diese in der Originaldatenbank definiert sind.

Beabsichtigen Sie, auch die Beziehungen zwischen den Tabellen in der Schemadatei zu speichern, müssen Sie die Beziehungen zwischen den Tabellen zuerst mit Programmcode definieren, bevor Sie die Methode WriteXmlSchema aufrufen. Erst dann können Sie sicher sein, dass mit ReadXmlSchema nicht nur die Metadaten der Tabellen, sondern gleichzeitig auch die definierten Tabellenbeziehungen in die XSD-Datei geschrieben werden.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2008
Visual C# 2008
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Visual C# 2012






 Visual C# 2012


Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de