28.2 Anatomie
Die vom Designer von Visual Studio erzeugte Datei NWDataSet.Designer.vb ist mit über 2000 Zeilen recht lang. In diesem Kapitel werde ich Sie durch wesentliche Teile führen.
28.2.1 Grobstruktur: Datentypen
Das stark typisierte DataSet ist von DataSet abgeleitet und enthält als innere Klassen einige öffentliche Datentypen, die die Tabellen, Zeilen und Zeilenänderungsereignisse repräsentieren. Damit ist die Datenbankstruktur beschrieben. Zusätzlich wird für jede Tabelle ein Table-Adapter erzeugt, der Methoden zum Beziehen und Ändern der Daten enthält. Schließlich ist der TableAdapterManager zum Abgleich der Daten mit der Datenbank gedacht, da er die referenzielle Integrität der Daten durch Steuerung der Aktualisierungsreihenfolge sicherstellt.
Class NWDataSet : Inherits DataSet ' Klassenmitglieder des nächsten Abschnitts ... Class ProductsDataTable : Inherits TypedTableBase(Of ProductsRow) Class CategoriesDataTable : Inherits TypedTableBase(Of CategoriesRow) Class ProductsRow : Inherits DataRow Class CategoriesRow : Inherits DataRow Class ProductsRowChangeEvent : Inherits EventArgs Class CategoriesRowChangeEvent : Inherits EventArgs End Class Namespace NWDataSetTableAdapters Class ProductsTableAdapter : Inherits Component Class CategoriesTableAdapter : Inherits Component Class TableAdapterManager : Inherits Component End Namespace
Hinweis |
Eine Basisklasse »TableAdapter« gibt es nicht. |
Hinweis |
Jede innere Klasse muss beim Gebrauch mit NWDataSet qualifiziert werden. |
28.2.2 Das typisierte DataSet
Das typisierte DataSet selbst enthält alle Funktionalität, um auf die Daten der Abfrage zuzugreifen. Da die gesamte Struktur bei der automatischen Erstellung des Datentyps aus der Datenbankabfrage ermittelt wurde, reicht ein parameterloser Konstruktor. Am wichtigsten sind die beiden streng typisierten Tabellen. Die beiden Delegates wären meiner Meinung nach besser in den zugehörigen Tabellen aufgehoben.
Class NWDataSet : Inherits DataSet Sub New() Function Clone() As DataSet ReadOnly Property Products() As ProductsDataTable ReadOnly Property Categories() As CategoriesDataTable ReadOnly Property Tables() As DataTableCollection ReadOnly Property Relations() As DataRelationCollection Property SchemaSerializationMode() As SchemaSerializationMode Shared Function GetTypedDataSetSchema(xs As XmlSchemaSet) _ As XmlSchemaComplexType Delegate Sub ProductsRowChangeEventHandler _ (sender As Object, e As ProductsRowChangeEvent) Delegate Sub CategoriesRowChangeEventHandler _ (sender As Object, e As CategoriesRowChangeEvent) ' Datentypen des vorigen Abschnitts ... End Class
Als erstes Beispiel soll ein typisiertes DataSet erzeugt werden. Durch die kleine Anweisung New NWDataSet() werden alle Eigenschaften und damit alle beteiligten Tabellen initialisiert (aber nicht mit Daten befüllt). Folglich sind dann bereits die gesamte Struktur der Tabellen und die Beziehungen untereinander bekannt. Das folgende Codefragment zeigt den Zugriff auf Spaltennamen und Fremdschlüsselbeziehungen (dessen Bedeutung ist an dieser Stelle unwichtig).
'...\ADO\DataSetTypisiert\TypisiertesDataSet.vb |
Option Strict On Imports System.Data.Common, System.Data.SqlClient Namespace ADO Module TypisiertesDataSet Sub Test() Dim nw As New NWDataSet() Console.Write("Spalten: ") For Each col As DataColumn In nw.Products.Columns Console.Write("{0} ", col.Caption) Next For Each rel As DataRelation In nw.Relations Console.Write(Environment.NewLine & "{0}: ", rel.RelationName) For no As Integer = 0 To rel.ChildColumns.Length – 1 Console.Write("{0}.{1} -> {2}.{3}", _ rel.ParentColumns(no).Table.TableName, _ rel.ParentColumns(no).Caption, _ rel.ChildColumns(no).Table.TableName, _ rel.ChildColumns(no).Caption) Next Next rel Console.ReadLine() End Sub End Module End Namespace
Hinweis |
Die Struktur eines typisierten DataSets ist ohne (erneuten) Kontakt zur Datenbank bekannt. |
28.2.3 TableAdapter
Im typisierten DatSet übernimmt der TableAdapter die Rolle des DataAdapters im untypisierten Fall. Er hat daher eine Fill-Methode, um die Tabelle mit Werten aus der Datenbank zu füllen, sowie Update-, Insert- und Delete-Methoden zur Änderung der Daten. Verbindungsdaten und Abfragekommandos werden bei der Erzeugung des TableAdapters direkt in den Quelltext geschrieben und in privaten Initialisierungsroutinen an einen SqlDataAdapter weitergereicht, der sich intern um die Kommunikation mit der Datenbank kümmert. Entsprechende öffentliche Methoden und Eigenschaften fehlen daher. Als Beispiel zeigt das nächste Codefragment den TableAdapter der Categories-Tabelle.
Class CategoriesTableAdapter : Inherits Component Sub New() Property ClearBeforeFill() As Boolean Function Fill(dataTable As NWDataSet.CategoriesDataTable) As Integer Function GetData() As NWDataSet.CategoriesDataTable Function Update(dataTable As NWDataSet.CategoriesDataTable) As Integer Function Update(dataSet As NWDataSet) As Integer Function Update(dataRow As DataRow) As Integer Function Update(dataRows() As DataRow) As Integer Function Update(CategoryName As String, Original_CategoryID As Integer, _ Original_CategoryName As String, CategoryID As Integer) As Integer Function Update(CategoryName As String, Original_CategoryID As Integer, _ Original_CategoryName As String) As Integer Function Insert(CategoryName As String) As Integer Function Delete(Original_CategoryID As Integer, _ Original_CategoryName As String) As Integer End Class
Alle Methodenparameter, die Tabellen beschreiben, müssen sich auf die Categories-Tabelle beziehen. Da diese als innere Klasse CategoriesDataTable von NWDataSet definiert ist (siehe Abschnitt 28.2.1, »Grobstruktur: Datentypen«), muss sie mit NWDataSet. qualifiziert werden. Zur Verdeutlichung sind Categories und NWDataSet kursiv gesetzt.
Hinweis |
Der in Visual Studio 2008 eingeführte TableAdapterManager ist für den Abgleich mit der Datenbank noch besser geeignet, da er auch während des Abgleichs die referenzielle Integrität der Datenbank sicherstellt. |
Als einfaches Beispiel zeigt das nächste Codefragment, wie die Products-Tabelle mit Werten aus der Datenbank befüllt wird. Dazu verwenden Sie den typisierten Adapter ProductsTableAdapter. Er ist im Namensraum NWDataSetTableAdapters definiert. Der Fill-Methode des Adapters übergeben Sie die typrichtige Products–Tabelle aus dem typisierten DataSet namens NWDataSet. Dies ist ganz analog zum DataAdapter (siehe Kapitel 25, »Data-Adapter«). Um das Beispiel einfach zu halten, wird hier nicht mit den Daten gearbeitet.
'...\ADO\DataSetTypisiert\TableAdapter.vb |
Option Strict On Imports System.Data.Common, System.Data.SqlClient Namespace ADO Module TableAdapter Sub Test() Dim nw As New NWDataSet() Dim nwa As New NWDataSetTableAdapters.ProductsTableAdapter() Dim zeilen As Integer = nwa.Fill(nw.Products) Console.WriteLine("{0} Zeilen der Tabelle {1} gelesen", _ zeilen, nwa.GetData().TableName) Console.ReadLine() End Sub End Module End Namespace
Die Ausgabe bestätigt den korrekten Zugriff:
77 Zeilen der Tabelle Products gelesen
Hinweis |
Ein explizites Connection- oder Command-Objekt ist beim typisierten TableAdapter überflüssig. Andererseits kann er nur mit der typrichtigen Tabelle verwendet werden. |
28.2.4 Typisierte Tabelle
Jede Tabelle in einem typisierten DataSet wird durch eine eigene Eigenschaft mit eigenem Datentyp repräsentiert. Dieser enthält für jede Spalte eine eigene Eigenschaft und eine Standardeigenschaft, um die Spalten über ihre Position anzusprechen. Außerdem wird der typkorrekte Zugriff auf Zeilen geregelt: das Hinzufügen und Löschen von Zeilen, inklusive eigener Ereignistypen, sowie das Suchen nach Primärschlüsseln. Als Beispiel zeigt das nächste Codefragment den Datentyp für die Categories-Tabelle.
Class CategoriesDataTable : Inherits TypedTableBase(Of CategoriesRow) Sub New() Function Clone() As DataTable ReadOnly Property CategoryIDColumn() As DataColumn ReadOnly Property CategoryNameColumn() As DataColumn ReadOnly Property Count() As Integer Default ReadOnly Property Item(index As Integer) As CategoriesRow Event CategoriesRowChanging As CategoriesRowChangeEventHandler Event CategoriesRowChanged As CategoriesRowChangeEventHandler Event CategoriesRowDeleting As CategoriesRowChangeEventHandler Event CategoriesRowDeleted As CategoriesRowChangeEventHandler Function NewCategoriesRow() As CategoriesRow Sub AddCategoriesRow(row As CategoriesRow) Function AddCategoriesRow(CategoryName As String) As CategoriesRow Function FindByCategoryID(CategoryID As Integer) As CategoriesRow Sub RemoveCategoriesRow(row As CategoriesRow) Shared Function GetTypedTableSchema(xs As XmlSchemaSet) As XmlSchemaComplexType End Class
28.2.5 Typisierte Zeile
Jeder Spalte eines typisierten DataSets wird eine Eigenschaft zugeordnet. Einerseits werden dadurch die Spalten ohne Anführungszeichen angegeben und von IntelliSense gefunden. Andererseits erzwingt die strenge Typisierung das korrekte Datenformat. Für jede Spalte, die Nullwerte erlaubt, sind außerdem noch Methoden zum Testen und Setzen eines Nullwerts vorhanden. Das nächste Codefragment zeigt als Beispiel den Zeilentyp der Products-Tabelle.
Class ProductsRow : Inherits DataRow
Property ProductID() As Integer
Property ProductName() As String
Property CategoryID() As Integer
Property UnitPrice() As Decimal
Property CategoriesRow() As CategoriesRow
Function IsCategoryIDNull() As Boolean
Sub SetCategoryIDNull()
Function IsUnitPriceNull() As Boolean
Sub SetUnitPriceNull()
End Class
Hinweis |
Da die Spaltenwerte nicht vom Typ Nullable sind, müssen Sie leider etwas Code hinzufügen, wenn Sie die Werte direkt an ein Control binden, um Nullwerte zu vermeiden. Außerdem sind die Zuweisungen spalte=Nothing und spalte=DBNull.Value verboten. |
Der Gebrauch ist sehr einfach. Statt des Spaltennamens wird die gleichnamige Eigenschaft verwendet. Das hat in der Praxis den unschätzbaren Vorteil, dass der Compiler Tippfehler erkennt, die bei einer Indizierung mit Zeichenketten erst zur Laufzeit auffallen.
zeile.ProductName = "Irgendwas" Console.WriteLine("Produkt {0}", zeile.ProductName)
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.