Klassengenerator

Gelegentlich ist es sinnvoll, Daten eines Datensatzes aus einer Tabelle in einem Objekt auf Basis einer Klasse abzulegen – vor allem dann, wenn Sie nur auf einen kompakten Satz von Daten zugreifen möchten und diese nicht ändern wollen. Leider ist der nötige Code je nach Anzahl der Felder der Datenherkunft und somit der Eigenschaften recht umfangreich und die Erstellung eine mühselige Arbeit. Mit dem in diesem Beitrag vorgestellten Add-In soll dies ganz schnell von der Hand gehen.

Die Beispieldatenbank ist ein fertiges Add-In. Zur Installation starten Sie den Add-In-Manager und wählen die Datei Klassengenerator.mda aus. Anschließend finden Sie im Add-In-Menü den Eintrag aiuKlassengenerator. Es erscheint der Dialog aus Bild 1. Hier lesen Sie entweder die Daten aller Tabellen neu ein (was beim ersten Öffnen in einer Datenbank automatisch geschieht) und wählen per Kombinationsfeld die Tabelle oder Abfrage aus, zu der Sie eine Klasse erstellen möchten. Das Unterformular zeigt dann alle Felder dieser Datenherkunft. Legen Sie dort fest, welche Felder als Eigenschaften der Klasse berücksichtigt werden sollen, und passen Sie gegebenenfalls von den Feldnamen abweichende Eigenschaftsnamen an. Klicken Sie dann auf Erzeugen, um die komplette Klasse auf Basis der Datenherkunft in Sekundenbruchteilen zu erstellen. Nach Wunsch fügen Sie dem Code auch gleich eine Prozedur hinzu, die eine Routine zum Erstellen und Füllen einer Instanz dieser Klasse erstellt. Die erzeugten Quelltexte müssen Sie nur noch in die Zielmodule kopieren.

pic015.png

Bild 1: Der Klassengenerator in Aktion

Datenmodell

Das Add-In soll die Auswahl einer Tabelle oder Abfrage ermöglichen, für deren Felder eine Klasse angelegt werden soll. Für diese Tabelle soll das Add-In alle Felder anzeigen. Der Benutzer legt fest, welche Felder berücksichtigt werden sollen und wie die entsprechenden Eigenschaften in der Klasse heißen sollen.

Das Add-In soll die Namen der Tabellen und Abfragen in der Datenbank, von der aus das Add-In gestartet wurde, in einem Kombinationsfeld zur Auswahl anbieten. Der direkte Zugriff von einem Kombinationsfeld innerhalb eines Add-Ins auf die Daten der Host-Datenbank ist nicht möglich, also schreiben wir die notwendigen Daten zuvor in eine Tabelle innerhalb der Add-In-Datenbank.

Diese Tabelle enthält die Felder TabelleAbfrageID (Primärschlüsselfeld, Autowert), TabelleAbfrage, Datenbank, Klassenname, Primaerschluesselfeld und Objekttyp. TabelleAbfrage nimmt die Namen der in der Datenbank enthaltenen Tabellen und Abfragen auf (s. Bild 2). Datenbank speichert den Namen der Zieldatenbank, damit einmal definierte Schemata zur Erzeugung von Klassen erneut verwendet werden können. Das Feld Klassenname nimmt den Namen auf, unter dem die Klasse für diese Tabelle oder Abfrage erzeugt werden soll – also beispielsweise clsPerson. Primaerschluesselfeld enthält den Namen des entsprechenden Feldes der Tabelle. Bei Tabellen wird dieser später automatisch ermittelt, bei Abfragen muss er manuell eingetragen werden. Objekttyp nimmt den Zahlenwert des Feldes Type der Tabelle MSysObjects auf – dazu später mehr.

pic001.png

Bild 2: Diese Tabelle speichert die Namen der Tabellen und Abfragen der Zieldatenbank.

Die zweite Tabelle heißt tblFelder (s. Bild 3). Sie enthält neben dem Primärschlüsselfeld FeldID folgende Felder:

pic003.png

Bild 4: Beziehung der beiden Tabellen des Add-Ins

  • Feldname: Name des Feldes in der Tabelle
  • Eigenschaftsname: Name der Eigenschaft für das Feld in der Klasse
  • Erzeugen: Gibt an, ob das Feld beim Erzeugen des Klassencodes berücksichtigt werden soll
  • TabelleAbfrageID: Fremdschlüsselfeld zur Tabelle tblTabellenAbfragen
  • Datentyp: Nummer des Datentyps des Feldes

Die Beziehung zwischen den beiden Tabellen ist mit referentieller Integrität definiert und Löschweitergabe. Wenn also ein Datensatz aus der Tabelle tblTabellenAbfragen gelöscht wird, entfernt Access auch die mit dem entsprechenden Datensatz verknüpften Datensätze der Tabelle tblFelder (s. Bild 4).

pic004.png

Bild 3: Die Tabelle tblFelder speichert die Felder und ihre Einstellungen.

Formular des Add-Ins

Damit der Benutzer die Tabelle/Abfrage für die Generierung der Klasse auswählen und die Felder festlegen kann, die das Add-In dabei berücksichtigen soll, zeigt das Add-In eine entsprechende Benutzeroberfläche in Form eines Formulars an. Dieses heißt frmKlassengenerator und bietet zunächst ein Kombinationsfeld zur Auswahl der zu verwendenden Tabelle oder Abfrage an. Außerdem soll das Hauptformular jeweils einen Datensatz der Tabelle tblTabelleAbfrage anzeigen – und zwar denjenigen, der vom Benutzer mit dem im Anschluss beschriebenen Kombinationsfeld ausgewählt wurde. Dazu stellen Sie für das Formular die Eigenschaft Datenherkunft auf die Tabelle tblTabelleAbfrage ein. Ziehen Sie außerdem die beiden Felder Datenbank und Klassenname aus der Feldliste in den Entwurf des Formulars. Lassen Sie darüber noch ein wenig Platz für ein Kombinationsfeld zur Auswahl der Tabelle/Abfrage (s. Bild 5). Wir schauen uns zunächst das Kombinationsfeld an und den Mechanismus, der die Datensatzherkunft des Kombinationsfeldes füllt und filtert.

pic005.png

Bild 6: Kombinationsfeld zur Auswahl der Tabellen und Abfragen der Zieldatenbank

Das Kombinationsfeld soll alle Tabellen der aktuellen Datenbank wie in Bild 6 anzeigen. Damit dieses überhaupt Daten anzeigt, weisen Sie diesem zunächst eine entsprechende Datenherkunft zu – und zwar die Tabelle tblTabellenAbfragen. Damit das Kombinationsfeld das Primärschlüsselfeld der Tabelle ausblendet und nur die Namen der Tabellen und Abfragen anzeigt, stellen Sie die beiden Eigenschaften Spaltenanzahl und Spaltenbreiten auf die Werte 2 und 0cm ein. Nachdem Sie in der Formularansicht geprüft haben, dass das Kombinationsfeld die Werte wie gewünscht anzeigt, können Sie die Eigenschaft Datensatzherkunft wieder leeren – wir füllen diese später zur Laufzeit. Immerhin soll das Kombinationsfeld ja nur die Tabellen und Abfragen der aktuellen Datenbank anzeigen. Die Tabelle tblTabellenAbfragen soll jedoch nach der Verwendung des Add-Ins in verschiedenen Anwendungen auch die entsprechenden Daten zu den jeweiligen Anwendungen enthalten.

pic002.png

Bild 5: Entwurfsansicht des aktuellen Stands des Formulars frmKlassengenerator

Deshalb löst das Formular beim Öffnen das Ereignis Beim Laden aus und somit die Ereignisprozedur aus Listing 1. Diese Prozedur speichert zunächst den Namen der Zieldatenbank in der Variablen strDatenbank (beim Erstellen und Testen des Add-Ins öffnen Sie dieses direkt und nicht von einer anderen Anwendung aus, daher enthält strDatenbank hier den Namen der Add-In-Datenbank selbst).

Listing 1: Füllen des Kombinationsfeldes beim Öffnen des Formulars

Private Sub Form_Load()
    Dim strDatenbank As String
    strDatenbank = CurrentProject.Name
    If IsNull(FLookup("TabelleAbfrageID", "tblTabellenAbfragen", "Datenbank = ''" & strDatenbank _
        & "''")) Then
    TabellenAbfragenEinlesen
End If
Me!cboTabelleAbfrage.RowSource = "SELECT * FROM tblTabellenAbfragen WHERE Datenbank = ''" _
    & strDatenbank & "''"
End Sub

Ein Aufruf der Funktion FLookup mit den entsprechenden Parametern prüft, ob es in der Tabelle tblTabellenAbfragen bereits mindestens einen Datensatz gibt, dessen Feld Datenbank den Namen der aktuellen Datenbank enthält. Falls ja, wird die Prozedur TabellenAbfragenEinlesen aufgerufen, welche die Tabelle tblTabellenAbfragen mit den Namen der Tabellen und Abfragen der Zieltabelle füllt. Anschließend stellt die Prozedur die Datensatzherkunft des Kombinationsfeldes cboTabelleAbfrage auf eine SQL-Abfrage ein, die alle Datensätze der Tabelle tblTabellenAbfragen liefert, deren Feld Datenbank den Namen der aktuellen Zieldatenbank enthält (beziehungsweise im Standalone-Modus den Namen der Add-In-Datenbank).

Warum nutzen wir hier eine Funktion namens FLookup – heißt diese nicht üblicherweise DLookup Das ist richtig. FLookup ist ein Nachbau der DLookup-Funktion, die allerdings auf eine Datensatzgruppe zugreift, die sich in der mit CodeDB referenzierten Datenbank befindet. DLookup greift, auch von einem Add-In aus, immer auf die Tabellen oder Abfragen der Datenbank zu, die das Add-In geöffnet hat.

Tabellen und Abfragen einlesen

Nun sind beim ersten Start des Formulars noch keine Tabellen oder Abfragen in der Tabelle tblTabellenAbfragen gespeichert. Also ruft die Prozedur Form_Load auf jeden Fall die Prozedur TabellenAbfragenEinlesen auf (s. Listing 2). Diese Prozedur deklariert gleich zwei Database-Objekte. Das erste heißt dbHost und wird mit der Funktion CurrentDb gefüllt. Dies liefert einen Verweis auf die aktuell in Access geöffnete Datenbank. Wenn Sie das Add-In von einer anderen Datenbank aus geöffnet haben, handelt es sich um die öffnende Datenbank, wenn Sie das Add-In direkt geöffnet haben, wird hier die Add-In-Datenbank selbst referenziert. Die zweite Variable namens dbAddIn erhält ihren Verweis auf das Database-Objekt über die Funktion CodeDB. Dieses liefert immer einen Verweis auf die Datenbank, in welcher sich der ausführende Code befindet.

Listing 2: Füllen der Tabelle tblTabellenAbfragen

Private Sub TabellenAbfragenEinlesen()
    Dim dbHost As DAO.Database
    Dim dbAddIn As DAO.Database
    Dim rst As DAO.Recordset
    Dim strKlassenname As String
    Set dbHost = CurrentDb
    Set dbAddIn = CodeDb
    Set rst = dbHost.OpenRecordset("SELECT Name, Type FROM MSysObjects WHERE Type IN (1,4,5,6) " _
        & "AND Name NOT LIKE ''~*'' ORDER BY Name", dbOpenDynaset)
    Do While Not rst.EOF
        strKlassenname = KlassennameErmitteln(rst!Name)
        dbAddIn.Execute "INSERT INTO tblTabellenAbfragen(TabelleAbfrageName, Datenbank, Klassenname) " _
            & "VALUES(''" & rst!Name & "'', ''" & CurrentProject.Name & "'', ''" & strKlassenname & "'')", _
        dbFailOnError
        rst.MoveNext
    Loop
    Me.Requery
    Me!cboTabelleAbfrage.Requery
    Set dbHost = Nothing
    Set dbAddIn = Nothing
End Sub

Mit diesen beiden Verweisen ausgestattet öffnet die Prozedur eine Datensatzgruppe auf Basis der Tabelle MSysObjects der Zieldatenbank. Die Datensatzgruppe liefert das Feld Name aller Datensätze zurück, deren Feld Type den Wert 1, 4, 5 oder 6 enthält und deren Name nicht mit ~ beginnt (dies sind Abfragen, die als Wert der Eigenschaften Datenherkunft beziehungsweise Datensatzherkunft gespeichert, aber nicht im Datenbankfenster oder Navigationsbereich angezeigt werden). Der Wert 1 für das Feld Type gibt an, dass es sich um eine eingebaute Tabelle handelt. 4 kennzeichnet eine per ODBC verknüpfte Tabelle, 5 eine Abfrage und 6 eine per Jet verknüpfte Tabelle – also beispielsweise eine externe Excel- oder Access-Tabelle oder Textdatei.

In einer Do While-Schleife über diese Datensätze trägt die Prozedur für jede Tabelle oder Abfrage einen Datensatz in die Tabelle tblTabellenAbfragen ein. Dazu ermittelt diese mit der Funktion KlassennameErmitteln den Namen der zu erzeugenden Klasse – mehr dazu weiter unten. Eine INSERT INTO-Anweisung füllt die Felder TabelleAbfrageName, Datenbank und Klassenname mit den ermittelten Werten. Anschließend aktualisiert die Prozedur die Datenherkunft des Formulars und des Kombinationsfeldes.

Felder der gewählten Datenherkunft anzeigen

Ein Unterformular namens sfmKlassengenerator liefert die Daten zu den in der Tabelle/Abfrage enthaltenen Feldern. Nach der Anzeige der Tabellen und Abfragen der aktuellen Datenbank und der Auswahl eines der Objekte durch den Benutzer soll das Unterformular alle Felder dieser Datenherkunft in der Datenblattansicht anzeigen. Dazu erstellen Sie zunächst ein Unterformular namens sfmKlassengenerator. Weisen Sie seiner Eigenschaft Datenherkunft die Tabelle tblFelder zu. Ziehen Sie die drei Felder Feldname, Eigenschaftsname und Erzeugen in den Detailbereich des Formularentwurfs und stellen Sie die Eigenschaft Standardansicht des Formulars auf Datenblatt ein (s. Bild 7).

pic006.png

Bild 7: Unterformular zur Anzeige der Felder der Tabelle oder Abfrage

Speichern Sie das Formular, öffnen Sie das Hauptformular frmKlassengenerator in der Entwurfsansicht und ziehen Sie das Unterformular sfmKlassengenerator aus dem Datenbankfenster beziehungsweise dem Navigationsbereich in den Detailbereich des Hauptformulars. Das Ergebnis sieht nun etwa wie in Bild 8 aus. Nun sorgen Sie dafür, dass nach der Auswahl einer Tabelle oder Abfrage durch den Benutzer die entsprechenden Felder im Unterformular angezeigt werden. Dazu müssen diese zunächst einmal in die Tabelle tblFelder eingetragen werden – dies ist bislang noch nicht geschehen.

pic007.png

Bild 8: Hauptformular mit integriertem Unterformular in der Entwurfsansicht

Dazu fügen Sie für das Kombinationsfeld cboTabelleAbfrage eine neue Ereignisprozedur für das Ereignis Nach Aktualisierung hinzu (s. Listing 3). Diese Prozedur verwendet wiederum zwei Objektvariablen des Typs Database – eines für die Add-In-Datenbank und eines für die aufrufende Datenbank. Die Prozedur prüft zunächst, ob der Benutzer überhaupt einen Eintrag im Kombinationsfeld cboTabelleAbfrage ausgewählt hat.

Listing 3: Füllen der Tabelle tblFelder

Private Sub cboTabelleAbfrage_AfterUpdate()
    Dim dbHost As DAO.Database
    Dim dbAddIn As DAO.Database
    Dim rst As DAO.Recordset
    Dim fld As DAO.Field
    Dim bolEinlesen As Boolean
    If Not IsNull(Me!cboTabelleAbfrage) Then
        bolEinlesen = True
        If Not IsNull(FLookup("FeldID", "tblFelder", "TabelleAbfrageID = " & Me!cboTabelleAbfrage)) Then
            bolEinlesen = _
                MsgBox("Felder neu einlesen und vorhandene Daten überschreiben", vbYesNo) = vbYes
        End If
        If bolEinlesen Then
            Set dbHost = CurrentDb
            Set dbAddIn = CodeDb
            dbAddIn.Execute "DELETE FROM tblFelder WHERE TabelleAbfrageID = " _
                & Me!cboTabelleAbfrage, dbFailOnError
            Set rst = dbHost.OpenRecordset("SELECT * FROM " & Me!cboTabelleAbfrage.Column(1) _
                & " WHERE 1=2", dbOpenDynaset)
            For Each fld In rst.Fields
                dbAddIn.Execute "INSERT INTO tblFelder(Feldname, Eigenschaftsname, " _
                    "TabelleAbfrageID, Erzeugen, Datentyp) VALUES(''" & fld.Name & "'', ''" _
                    & fld.Name & "'', " & Me!cboTabelleAbfrage & ", -1, " & fld.Type & ")", _
                dbFailOnError
            Next fld
        End If
        Me!sfmKlassengenerator.Form.Requery
        Me.Filter = "TabelleAbfrageID = " & Me!cboTabelleAbfrage
        Me.FilterOn = True
    End If
End Sub

Sie haben das Ende des frei verfügbaren Textes erreicht. Möchten Sie ...

TestzugangOder bist Du bereits Abonnent? Dann logge Dich gleich hier ein. Die Zugangsdaten findest Du entweder in der aktuellen Print-Ausgabe auf Seite U2 oder beim Online-Abo in der E-Mail, die Du als Abonnent regelmäßig erhältst:

Schreibe einen Kommentar