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

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 28 Stark typisierte DataSets
Pfeil 28.1 Stark typisierte DataSets erzeugen
Pfeil 28.1.1 Typisierte DataSets mit dem Visual Studio Designer erstellen
Pfeil 28.1.2 Das Kommandozeilentool »XSD.exe«
Pfeil 28.2 Anatomie eines typisierten DataSet
Pfeil 28.2.1 Datenzeilen einer Tabelle ausgeben
Pfeil 28.2.2 Datenzeilen hinzufügen
Pfeil 28.2.3 Datenzeilen bearbeiten
Pfeil 28.2.4 Datenzeilen suchen
Pfeil 28.2.5 NULL-Werte im typisierten DataSet
Pfeil 28.2.6 Daten in einem hierarchischen DataSet
Pfeil 28.3 Typisierte DataSets manuell im Designer erzeugen
Pfeil 28.3.1 DataTable manuell erzeugen
Pfeil 28.3.2 Der DataTable Spalten hinzufügen
Pfeil 28.3.3 Beziehungen zwischen den Tabellen erstellen
Pfeil 28.3.4 Weitergehende Betrachtungen
Pfeil 28.4 Der TableAdapter
Pfeil 28.4.1 TableAdapter mit Visual Studio erzeugen
Pfeil 28.4.2 Die Methode »Fill« des TableAdapters
Pfeil 28.4.3 Die Methode »GetData«
Pfeil 28.4.4 Die Methode »Update«
Pfeil 28.4.5 Aktualisieren mit den »DBDirect«-Methoden
Pfeil 28.4.6 TableAdapter mit mehreren Abfragen
Pfeil 28.4.7 Änderungen an einem TableAdapter vornehmen
Pfeil 28.5 Fazit: Typisierte oder nicht typisierte DataSets?


Galileo Computing - Zum Seitenanfang

28.2 Anatomie eines typisierten DataSet Zur nächsten ÜberschriftZur vorigen Überschrift

Werfen wir einen Blick in die Klassendatei des typisierten DataSet. Öffnen Sie dazu die Datei NWDataSet.Designer.cs im Editor.

Die Datei enthält mehrere Klassendefinitionen:


public partial class NWDataSet : DataSet
public partial class ProductsTableAdapter : Component
public partial class CategoriesTableAdapter : Component

Die Klassendefinition, die in direktem Zusammenhang mit dem typisierten DataSet steht, ist die Klasse NWDataSet. Diese Klasse ist von DataSet abgeleitet. Des Weiteren enthält die Datei für jede der beiden Tabellen eine TableAdapter-Klassendefinition: ProductsTableAdapter und CategoriesTableAdapter. Die Letztgenannten sind direkt von System.ComponentModel.Component abgeleitet. Darauf werde ich später noch eingehen.

Die Klasse NWDataSet ist prall mit automatisch generiertem Code gefüllt. Sie enthält unter anderem auch die beiden Klassen CategoriesDataTable und ProductsDataTable. Beide sind von DataTable abgeleitet. Um die Datenzeilen der beiden Tabellen zugänglich zu machen, sind im typisierten DataSet auch noch die beiden Klassen CategoriesRow und ProductsRow spezifiziert, die die gemeinsame Basisklasse DataRow haben.


Galileo Computing - Zum Seitenanfang

28.2.1 Datenzeilen einer Tabelle ausgeben Zur nächsten ÜberschriftZur vorigen Überschrift

Damit Sie sehen, wie mit einem typisierten DataSet gearbeitet wird, möchte ich Ihnen zunächst ein ganz einfaches Beispiel vorstellen. Es basiert auf dem eben erzeugten typisierten DataSet, das mit NWDataSet bezeichnet worden ist. Dieses DataSet bildet die Grundlage, um uns alle Produktnamen an der Konsole auszugeben. Hier ist zuerst der dazu notwendige Code:


// ---------------------------------------------------------
// Beispiel: ...\Kapitel 28\TypisiertesDataSet
// ---------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using TypisiertesDataSet.NWDataSetTableAdapters;
namespace TypisiertesDataSet {
  class Program {
    static void Main(string[] args) {
      NWDataSet ds = new NWDataSet();
      ProductsTableAdapter tblAd = new ProductsTableAdapter();
      tblAd.Fill(ds.Products);
      foreach(NWDataSet.ProductsRow row in ds.Products) {
        Console.WriteLine("{0}", row.ProductName);
      }
      Console.ReadLine();
    }
  }
}

Wir benötigen weder ein SqlConnection-Objekt noch ein SqlCommand-Objekt. Alle Informationen, die wir bisher diesen Objekten mitgeteilt haben, stecken nun im typisierten DataSet. Wie Sie weiter oben gesehen haben, lautet dessen Klassenbezeichner NWDataSet. Diese Klasse wird als Erstes instanziiert.

In der Datei NWDataSet.Designer.cs ist nicht nur NWDataSet definiert, sondern darüber hinaus auch die beiden Klassen ProductsTableAdapter und CategoriesTableAdapter. Ein TableAdapter ist ein Objekt, das zum Laden und Speichern einer Tabelle im typisierten DataSet dient und zusammen mit der Klasse des typisierten DataSet automatisch erzeugt wird.

Schauen Sie sich noch einmal den Code in der Datei NWDataSet.Designer.cs an. Sie werden feststellen, dass die beiden TableAdapter-Klassen dem Namespace TypisiertesDataSet.NWDataSetTableAdapters angehören, einem dem Stamm-Namespace untergeordneten Namespace also. Aus diesem Grund ist der Namespace der Klasse TableAdapter mit using bekannt gegeben worden.

Ähnlich wie ein DataAdapter muss auch ein TableAdapter instanziiert werden. Gefüllt wird das DataSet durch den Aufruf von Fill des TableAdapters. Allerdings ist diese Methode typisiert, ihr muss also ein ganz bestimmter Datentyp übergeben werden. Wenn Sie sich die Definition der Methode Fill in der Klasse ProductsTableAdapter ansehen, wissen Sie, welche Tabelle mit Daten gefüllt werden kann:


public virtual int Fill(NWDataSet.ProductsDataTable dataTable)

Die Fill-Methode, die auf der Referenz eines ProductsTableAdapters aufgerufen wird, kann demnach nur ein Objekt vom Typ NWDataSet.ProductsDataTable füllen. (Beachten Sie, dass die Klasse ProductsDataTable eine innere Klasse der Klasse NWDataSet ist. Öffentliche innere Klassen können instanziiert werden, allerdings beinhaltet der Klassenbezeichner immer die Nennung der äußeren Klasse. Sie wird, durch einen Punkt getrennt, vor den Namen der inneren Klasse gesetzt.

Doch was ist nun als Argument der Methode Fill zu übergeben? Die Antwort liefert wieder ein Blick in die Klasse NWDataSet. Das folgende Codefragment zeigt den in diesem Zusammenhang wichtigen Ausschnitt:


public partial class NWDataSet : DataSet {
  private ProductsDataTable tableProducts;
  private CategoriesDataTable tableCategories;
  private void InitClass() {
    ...
    this.tableProducts = new ProductsDataTable();
    ...
    this.tableCategories = new CategoriesDataTable();
  }
  public ProductsDataTable Products {
    get {
      return this.tableProducts;
    }
}

Auf Klassenebene sind die beiden Variablen tableProducts und tableCategories deklariert. In der Methode InitClass, die intern bei der Instanziierung des typisierten DataSet aufgerufen wird, erfolgt die Initialisierung der beiden Variablen. Genau genommen beherbergt das typisierte DataSet also die beiden Tabellen Products und Categories. Die Referenz auf die beiden Tabellen wird über schreibgeschützte Eigenschaftsmethoden offengelegt, von denen im Codefragment nur die Eigenschaftsmethode der Products-Tabelle angegeben ist. Daher muss das typisierte DataSet NWDataSet mit


tblAd.Fill(ds.Products);

gefüllt werden.

Es gilt nun, die Datenzeilen der Products-Tabelle zu durchlaufen. In der Klasse NWDataSet sind dazu die beiden inneren Klassen ProductsRow und CategoriesRow definiert. Sie ersetzen im typisierten DataSet die generische Klasse DataRow. Doch wie kommt man an ein Array oder eine Auflistung aller Datenzeilen einer Tabelle?

Jetzt wird das Klassenkonzept interessant. Das ProductsDataTable-Objekt repräsentiert nämlich genau genommen nicht nur die Tabelle Products, sondern gleichzeitig auch alle darin enthaltenen Datenzeilen. Sie sehen das bestätigt, wenn Sie sich die Klassendefinition von ProductsDataTable ansehen. Hier finden Sie die Überladung des this-Operators, der als Parameter einen Integer erwartet, der als Index einer bestimmten Datenzeile interpretiert wird.


public ProductsRow this[int index] {
  get {
    return ((ProductsRow)(this.Rows[index]));
  }
}

Mit dieser Erkenntnis kann nun auch die foreach-Schleife konstruiert werden:


foreach(NWDataSet.ProductsRow row in ds.Products)
  ...

Sie haben nun gesehen, wie Sie sich mit einem typisierten DataSet die Datenzeilen einer der beiden Tabellen ausgeben lassen können. Wahrscheinlich bleibt bei Ihnen ein flaues Gefühl im Magen zurück. Um dasselbe Resultat zu erzielen, hätten Sie vermutlich effektiver mit den generischen Klassen von ADO.NET gearbeitet. Ganz unrecht kann ich Ihnen nicht geben. Auch mir ging es am Anfang nicht anders, als ich mich zum ersten Mal mit den typisierten DataSets auseinandergesetzt habe. Aber wir sind mit der Thematik auch noch nicht am Ende angelangt, denn mit einem typisierten DataSet werden noch viele andere Dinge möglich – und vielleicht sogar einfacher.

Zugegeben, ein wenig abschreckend und auch unübersichtlich sind die automatisch vom Designer erzeugten Klassendefinitionen schon. Um Ihnen ein wenig die Hemmung davor zu nehmen, bin ich in diesem Abschnitt etwas intensiver auf deren Code eingegangen – zumindest im Zusammenhang mit der selbst auferlegten Aufgabenstellung. Wenn Sie mit typisierten DataSets arbeiten, werden Sie nicht umhinkommen, den Code eventuell an die eigenen Anforderungen und Bedürfnisse anzupassen. Werfen Sie daher ruhig auch einen Blick in die Klassendefinitionen, wenn ich Ihnen in den folgenden Abschnitten weitere Möglichkeiten des Einsatzes der typisierten DataSets vorstellen werde.


Galileo Computing - Zum Seitenanfang

28.2.2 Datenzeilen hinzufügen Zur nächsten ÜberschriftZur vorigen Überschrift

Um eine Datenzeile zu einer typisierten Tabelle hinzuzufügen, wird in der typisierten Tabelle eine Methode mit der folgenden Namenskonvention bereitgestellt:


New<Tabellenname>Row

Diese Methode ersetzt die Methode NewRow in einem untypisierten DataSet. Ferner finden wir auch eine Methode, um die neue Datenzeile zur Auflistung der Datenzeilen hinzuzufügen:


Add<Tabellenname>Row

Es stellt sich nun nur noch die Frage, wie man die Werte in die neue Datenzeile eintragen kann. Das ist sehr einfach, weil die Spaltenbezeichner der zugrunde liegenden Abfrage als gleichnamige Eigenschaften der typisierten DataRows abgebildet werden. Im folgenden Codefragment wird das deutlich:


NWDataSet ds = new NWDataSet();
ProductsTableAdapter tblAd = new ProductsTableAdapter();
// Tabelle füllen
tblAd.Fill(ds.Products); 
// neue Datenzeile
NWDataSet.ProductsRow row = ds.Products.NewProductsRow();
row.ProductName = "Gewürzgurke";
row.CategoryID = 2;
row.UnitPrice = 100;
ds.Products.AddProductsRow(row);

Sie werden mir zustimmen, dass dieser Code recht intuitiv aussieht und einfach zu lesen ist. Sie werden auch feststellen, dass Sie bei der Codierung durch die IntelliSense-Hilfe eine hervorragende Unterstützung erfahren. Zumindest haben Sie gut lesbaren Programmcode, ohne dass Sie explizit die Spaltenbezeichner angeben müssen (falls Sie keine Indizes verwenden).

Wie die Add-Methode der DataRowCollection ist auch die Methode Add<Tabellenname>Row der typisierten DataTable überladen. Rufen Sie diese Überladung auf, wird intern automatisch eine temporäre Datenzeile mit allen Spalten angelegt, und die Spalten werden mit den Daten aus der Argumentenliste gefüllt. Anschließend wird die neue Datenzeile an die Auflistung der Datenzeilen angehängt.


ds.Products.AddProductsRow("Gewürzgurke",1, 100);


Galileo Computing - Zum Seitenanfang

28.2.3 Datenzeilen bearbeiten Zur nächsten ÜberschriftZur vorigen Überschrift

Das Ändern von Datenzeilen ähnelt dem Vorgehen in einem nicht typisierten DataSet. Es stehen Ihnen auch in einem typisierten DataSet die Methoden BeginEdit, EndEdit und CancelEdit zur Verfügung.


// zu ändernde Datenzeile referenzieren
NWDataSet.ProductsRow row = ds.Products[0]; 
// gewünschte Spalte ändern
row.BeginEdit();
row.ProductName = "Hartkäse";
row.EndEdit();

Alternativ dürfen Sie auch ohne vorherigen Aufruf von BeginEdit sofort den Wert in die betreffende Spalte schreiben. Die Möglichkeit, die Änderung zurückzunehmen, haben Sie dann allerdings nicht.


Galileo Computing - Zum Seitenanfang

28.2.4 Datenzeilen suchen Zur nächsten ÜberschriftZur vorigen Überschrift

Eine Datenzeile in einer typisierten DataTable anhand ihres Primärschlüssels zu finden ist eine ganz simple Angelegenheit. Jede typisierte Tabelle veröffentlicht dazu eine typisierte Find-Methode. In der typisierten Products-Tabelle lautet sie FindByProductID, in der typisierten Categories-Tabelle FindByCategoryID.


NWDataSet.ProductsRow row = ds.Products.FindByProductID(5);

Handelt es sich um einen kombinierten Primärschlüssel aus mehreren Spalten, wie beispielsweise in der Tabelle Order Details, werden in den Bezeichner der Find-Methode die Namen der Spalten übernommen, die den Primärschlüssel bilden. In dieser Reihenfolge geben Sie auch die Werte für die Spalten in der Argumentenliste an.


Galileo Computing - Zum Seitenanfang

28.2.5 NULL-Werte im typisierten DataSet Zur nächsten ÜberschriftZur vorigen Überschrift

Jede typisierte DataRow, die NULL-Werte enthalten kann, bietet zwei Methoden an, um mit den NULL-Werten zu arbeiten. Die erste Methode dient dazu, Werte einer Spalte auf NULL zu setzen, die zweite Methode dient dazu, den Wert einer Spalte auf NULL zu untersuchen. In der Tabelle Products ist die Spalte UnitPrice eine der Spalten, die NULL-Werte erlaubt. Die beiden Methoden, die im Zusammenhang mit den NULL-Werten stehen, heißen SetUnitPriceNull und IsUnitPriceNull.


NWDataSet ds = new NWDataSet();
ProductsTableAdapter tblAd = new ProductsTableAdapter();
// Tabelle füllen
tblAd.Fill(ds.Products); 
// neue Datenzeile hinzufügen
NWDataSet.ProductsRow newRow = ds.Products.NewProductsRow();
newRow.ProductName = "Gewürzgurke";
newRow.CategoryID = 2; 
// Wert der Spalte 'UnitPrice' auf NULL setzen
newRow.SetUnitPriceNull();
ds.Products.AddProductsRow(newRow); 
// Datenzeilenliste ausgeben
foreach(NWDataSet.ProductsRow row in ds.Products) {
  Console.Write("{0,-4}", row.ProductID);
  Console.Write("{0,-35}", row.ProductName);
  if (!row.IsUnitPriceNull())
    Console.WriteLine("{0}", row.UnitPrice);
  else
    Console.WriteLine("NULL");
}


Galileo Computing - Zum Seitenanfang

28.2.6 Daten in einem hierarchischen DataSet topZur vorigen Überschrift

In einem nicht typisierten DataSet können Sie mit den Methoden GetChildRows, GetParentRow und GetParentRows durch ein hierarchisches DataSet navigieren. Dabei müssen Sie nicht nur die DataRelation angeben, über die die Daten abgefragt werden, sondern darüber hinaus auch die beiden verknüpften Spalten aufseiten der Master- und Detailtabelle.

Beim Erzeugen eines typisierten DataSet wird automatisch die Beziehung zwischen zwei oder auch mehr Tabellen erkannt. Sie konnten das im Designer erkennen. Es wird daher ein wenig einfacher, durch das typisierte DataSet zu navigieren. Dazu stellt die Klasse CategoriesRow die Methode GetProductsRows bereit, die ohne Übergabe eines Arguments aufgerufen werden kann und alle verknüpften Datenzeilen der Detailtabelle liefert.

Um von einer Detailtabelle auf eine verknüpfte Datenzeile in der Mastertabelle zuzugreifen, weicht der Methodenbezeichner leider die eingeschlagene Konvention auf und verzichtet auf das Präfix Get. Hier heißt die Methode schlichtweg CategoriesRow.


NWDataSet ds = new NWDataSet();
ProductsTableAdapter adProducts = new ProductsTableAdapter();
CategoriesTableAdapter adCategories = new CategoriesTableAdapter();
// Tabelle füllen
adProducts.Fill(ds.Products);
adCategories.Fill(ds.Categories); 
// Datenzeilen der Tabelle 'Categories' durchlaufen
foreach (NWDataSet.CategoriesRow rowCat in ds.Categories) {
  Console.WriteLine("Kategorie: {0}\n", rowCat.CategoryName); 
  // Datenzeilen der Tabelle 'Products' durchlaufen
  foreach (NWDataSet.ProductsRow rowProduct in rowCat.GetProductsRows())
  {
    Console.WriteLine("  {0}", rowProduct.ProductName);
  }
  Console.WriteLine(new string('-',40));
}



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# 2010

Visual C# 2010
Jetzt bestellen


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

 Buchempfehlungen
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


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2010
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.


Nutzungsbestimmungen | Datenschutz | Impressum

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

Cookie-Einstellungen ändern