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

Inhaltsverzeichnis
Vorwort zur 6. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Das Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Strukturen und Enumerationen
7 Fehlerbehandlung und Debugging
8 Auflistungsklassen (Collections)
9 Generics – Generische Datentypen
10 Weitere C#-Sprachfeatures
11 LINQ
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 XML
15 Multithreading und die Task Parallel Library (TPL)
16 Einige wichtige .NET-Klassen
17 Projektmanagement und Visual Studio 2012
18 Einführung in die WPF und XAML
19 WPF-Layout-Container
20 Fenster in der WPF
21 WPF-Steuerelemente
22 Elementbindungen
23 Konzepte von WPF
24 Datenbindung
25 Weitere Möglichkeiten der Datenbindung
26 Dependency Properties
27 Ereignisse in der WPF
28 WPF-Commands
29 Benutzerdefinierte Controls
30 2D-Grafik
31 ADO.NET – Verbindungsorientierte Objekte
32 ADO.NET – Das Command-Objekt
33 ADO.NET – Der SqlDataAdapter
34 ADO.NET – Daten im lokalen Speicher
35 ADO.NET – Aktualisieren der Datenbank
36 Stark typisierte DataSets
37 Einführung in das ADO.NET Entity Framework
38 Datenabfragen des Entity Data Models (EDM)
39 Entitätsaktualisierung und Zustandsverwaltung
40 Konflikte behandeln
41 Plain Old CLR Objects (POCOs)
Stichwort

Download:
- Beispiele, ca. 62,4 MB

Jetzt Buch bestellen
Ihre Meinung?

Spacer
Visual C# 2012 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2012

Visual C# 2012
Rheinwerk Computing
1402 S., 6., aktualisierte und erweiterte Auflage 2013, geb., mit DVD
49,90 Euro, ISBN 978-3-8362-1997-6
Pfeil 36 Stark typisierte DataSets
Pfeil 36.1 Ein stark typisiertes DataSet erzeugen
Pfeil 36.1.1 Typisierte DataSets mit dem Visual Studio Designer erstellen
Pfeil 36.1.2 Das Kommandozeilentool XSD.exe
Pfeil 36.2 Die Anatomie eines typisierten DataSets
Pfeil 36.2.1 Die Datenzeilen einer Tabelle ausgeben
Pfeil 36.2.2 Datenzeilen hinzufügen
Pfeil 36.2.3 Datenzeilen bearbeiten
Pfeil 36.2.4 Datenzeilen suchen
Pfeil 36.2.5 NULL-Werte im typisierten DataSet
Pfeil 36.2.6 Die Daten in einem hierarchischen DataSet
Pfeil 36.3 Typisierte DataSets manuell im Designer erzeugen
Pfeil 36.3.1 Eine »DataTable« manuell erzeugen
Pfeil 36.3.2 Der »DataTable« Spalten hinzufügen
Pfeil 36.3.3 Beziehungen zwischen den Tabellen erstellen
Pfeil 36.4 Weiter gehende Betrachtungen
Pfeil 36.5 Der »TableAdapter«
Pfeil 36.5.1 Einen »TableAdapter« mit Visual Studio erzeugen
Pfeil 36.5.2 Die Methode »Fill« des »TableAdapters«
Pfeil 36.5.3 Die Methode »GetData«
Pfeil 36.5.4 Die Methode »Update«
Pfeil 36.5.5 Aktualisieren mit den DBDirect-Methoden
Pfeil 36.5.6 TableAdapter mit mehreren Abfragen
Pfeil 36.5.7 Änderungen an einem »TableAdapter« vornehmen
Pfeil 36.6 Fazit: Typisierte oder nicht typisierte DataSets?

Rheinwerk Computing - Zum Seitenanfang

36.2 Die Anatomie eines typisierten DataSetsZur nächsten Überschrift

Werfen wir einen Blick in die Klassendatei des typisierten DataSets. Ö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.


Rheinwerk Computing - Zum Seitenanfang

36.2.1 Die Datenzeilen einer Tabelle ausgebenZur 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 zuerst der dazu notwendige Code.

// Beispiel: ..\Kapitel 36\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();
}
}
}

Listing 36.2 Beispielprogramm »TypisiertesDataSet«

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 DataSets 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 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 durch einen Punkt getrennt vorangestellte Nennung der äußeren Klasse.)

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

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;
}
}

Listing 36.3 Ausschnitt aus der Klasse »NWDataSet«

Auf Klassenebene sind die beiden Variablen tableProducts und tableCategories deklariert. In der Methode InitClass, die intern bei der Instanziierung des typisierten DataSets 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 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]));
}
}

Listing 36.4 Indexerdefinition

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, 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.


Rheinwerk Computing - Zum Seitenanfang

36.2.2 Datenzeilen hinzufügenZur 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);

Listing 36.5 Daten an eine neue Datenzeile übergeben

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);

Rheinwerk Computing - Zum Seitenanfang

36.2.3 Datenzeilen bearbeitenZur 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];
// Die gewünschte Spalte ändern
row.BeginEdit();
row.ProductName = "Hartkäse";
row.EndEdit();

Listing 36.6 Editieren einer Datenzeile

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.


Rheinwerk Computing - Zum Seitenanfang

36.2.4 Datenzeilen suchenZur 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.


Rheinwerk Computing - Zum Seitenanfang

36.2.5 NULL-Werte im typisierten DataSetZur 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 hin 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;
// Den 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");
}

Listing 36.7 NULL-Einträge behandeln


Rheinwerk Computing - Zum Seitenanfang

36.2.6 Die Daten in einem hierarchischen DataSetZur 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 auf Seiten der Master- und Detailtabelle.

Beim Erzeugen eines typisierten DataSets 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));
}

Listing 36.8 Daten eines hierarchischen DataSets



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.

<< zurück
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Visual C# 2012

Visual C# 2012
Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Rheinwerk-Shop: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: C/C++






 C/C++


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo





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