Manche kennen vielleicht das Problem, auf das man beim Befüllen von Feldern einer bestimmten Klasse durch einen SQLDataReader stößt. Und zwar muss man jedes mal, wenn man die Klasse um ein Feld oder eine Property erweitert, an der entsprechenden Stelle im Code auch dessen Wert zuweisen (zumindest, wenn man sich nicht vom Visual Studio Assistenten eine Klasse bzw. ein Dataset erstellen lässt).
Bisher habe ich sowas für kleinere Programme in etwa wie folgt gemacht:
Public Class Mitarbeiter
' Hier eine Beispiel-Klasse für einen Mitarbeiter,
' den wir aus der Datenbank auslesen möchten.
Public VORNAME As String
Public NACHNAME As String
Public ANREDE As String
Public TELEFON As String
Public TELEFAX As String
Public MAIL As String
Public ART As String
Public STATUS As String
End Class
Public Class MitarbeiterListe
' Dies ist die Liste aller Mitarbeiter aus der Datenbank,
' die wir einlesen wollen.
' Beim Erstellen einer neuen Instanz der Klasse MitarbeiterListe,
' wird die Datenbank geöffnet und für jede
' zurückgelieferte Zeile eine neue Instanz der Klasse Mitarbeiter
' erstellt und ihr die Werte der verschiedenen Spalten zugewiesen.
Inherits List(Of Mitarbeiter)
Public Sub New()
' Als erstes versuchen wir die Verbindung zur Datenbank zu öffnen
Try
Using con As New SqlClient.SqlConnection(My.Settings.SQLConnectionString)
Try
con.Open()
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
Exit Sub
End Try
' Dann erstellen wir den Command zur Auflistung unserer Mitarbeiter-Query
Dim cmd As New SqlClient.SqlCommand("Select * From viewMitarbeiter", con)
' und führen die Abfrage aus
Using dr As SqlClient.SqlDataReader = cmd.ExecuteReader
' Nun gehen durch alle zurückgelieferten Datensätze
Do While dr.Read
' Neuen Mitarbeiter erstellen
Dim itm As New Mitarbeiter
' Werte einzeln den jeweiligen Fields zuweisen
with itm
.Vorname = dr.Item("VORNAME")
.Nachname = dr.Item("NACHNAME")
.Telefon = dr.Item("TELEFAX")
.Telefax = dr.Item("TELEFAX")
' usw. usf.
end with
' Und zur Liste hinzufügen
Me.Add(itm)
Loop
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
Exit Sub
End Try
End Sub
End ClassMittlerweile mache ich das wie folgt:
Public Class MitarbeiterListe
Inherits List(Of Mitarbeiter)
Public Sub New()
' Als erstes versuchen wir die Verbindung zur Datenbank zu öffnen
Try
Using con As New SqlClient.SqlConnection(My.Settings.SQLConnectionString)
Try
con.Open()
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
Exit Sub
End Try
' Dann erstellen wir den Command zur Auflistung unserer Mitarbeiter-Query
Dim cmd As New SqlClient.SqlCommand("Select * From viewMitarbeiter", con)
' und führen die Abfrage aus
Using dr As SqlClient.SqlDataReader = cmd.ExecuteReader
' Nun gehen durch alle zurückgelieferten Datensätze
Do While dr.Read
' Neuen Mitarbeiter erstellen
Dim itm As New Mitarbeiter
' Werte dynamisch den Fields zuweisen
DataFiller.SetFieldsFromDataReader(itm, dr)
' Und zur Liste hinzufügen
Me.Add(itm)
Loop
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
Exit Sub
End Try
End Sub
End ClassUnd hier nun meine neue Klasse, die aus der Klasse aObj sämtliche Felder liest und ihr die Werte dynamisch aus dem übergebenen SQLDataReader zuweist.
Public Class DataFiller
Public Shared Sub SetFieldsFromDataReader(ByVal aObj As Object, ByVal aDataReader As SqlClient.SqlDataReader)
Dim fis As System.Reflection.FieldInfo() = aObj.GetType().GetFields()
' For each column in the datareader
For i As Integer = 0 To aDataReader.FieldCount - 1
' Only assign if we have a value
If Not aDataReader.IsDBNull(i) Then
' Check for Field-Name match for each field in our aObj Class
For Each fi As System.Reflection.FieldInfo In fis
If fi.Name = aDataReader.GetName(i) Then
Try
fi.SetValue(aObj, aDataReader.Item(i), Reflection.BindingFlags.SetField, Nothing, Nothing)
Exit For
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
End If
Next
End If
Next
End Sub
End ClassWas meine obige Methode SetFieldsFromDataReader beispielsweise noch nicht kann, ist mit Properties zu arbeiten. Sie arbeitet lediglich mit Feldern. Auch muss man dringend noch die ein oder andere Fehlerbehandlung integrieren. Und optimieren lässt sich auch noch einiges beim Durchlaufen der Schleife. Beispielsweise könnte man vor dem Aufruf von SetFieldsFromDataReader alle Fields der jeweiligen Klasse auslesen und mit übergeben. So würden dann nicht jedes Mal beim Aufruf der Methode immer wieder die Fields ermittelt...
Sollte jemand eine einfachere, oder bessere Lösung haben, würde ich mich sehr freuen, darüber zu hören.
Update am 01.12.2009:
Da ich immer häufiger auf die obigen Funktionen zugreife, habe ich meine Klasse DataFiller mittlerweile etwas ergänzt. Wer möchte, kann sich den Quellcode hier herunter laden.