Von Access zu Entity Framework: Update 1

Wenn Sie ein Abonnement des Magazins 'DATENBANKENTWICKLER' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Von Access zu Entity Framework: Update 1

Fehler bei Verwendung gleicher Variablennamen für Objekte und Collections

Bild 1: Fehler bei Verwendung gleicher Variablennamen für Objekte und Collections

In Ausgabe 5/2018 haben wir in zwei Artikeln gezeigt, wie Sie das Datenmodell und die Daten einer Access-Datenbank in ein Entity Data Model und darüber in eine SQL Server-Datenbank migrieren. Im vorliegenden Artikel finden Sie eine Optimierung der dort beschriebenen Prozeduren. Im Detail geht es darum, dass in Access-Tabellen manchmal Namen in Tabellen verwendet werden, die gleichzeitig Plural und Singular der enthaltenen Entität sind – wie zum Beispiel bei tblArtikel. Das führt bei unserer automatisierten Migration früher oder später zu Problemen. Daher erweitern wir unsere Prozeduren um die notwendigen Unterscheidungen.

Im Artikel Access zu WPF: Detailformulare mit Textfeldern aus der aktuellen Ausgabe beispielsweise erstellen wir das Code behind-Modul für eine WPF-Detailansicht der Einträge der Tabelle tblKunden beziehungsweise der Collection Kunden und der Klasse Kunde. Hier gelingt alles reibungslos, da wir beim Migrieren der Tabellen der Access-Tabelle in die DbSet-Definition und die Klassendefinition mit Kunde und Kunden entsprechende unterschiedliche Bezeichnungen verwenden konnten. Wenn wir die dort erarbeiteten Prozeduren nutzen wollen, um eine Detailansicht auf Basis eines Formulars zu erstellen, dass die Tabelle tblArtikel als Datensatzquelle verwendet, gelingt das nicht problemlos, da beim Migrieren des Datenmodells in das Data Entity Model aus tblArtikel sowohl das DbSet-Element Artikel als auch die Klasse Artikel generiert wurden. Das führt noch nicht zu Fehlern, aber wenn wir dann mit der Prozedur FormularNachWPF_CodeBehind die Code behind-Klasse erzeugen und dort einfügen, erhalten wir eine Reihe von Fehlermeldungen (siehe Bild 1).

Fehler bei Verwendung gleicher Variablennamen für Objekte und Collections

Bild 1: Fehler bei Verwendung gleicher Variablennamen für Objekte und Collections

Wie können wir das Problem lösen? Eine Möglichkeit wäre, die Bezeichnungen im Code etwa der Code behind-Klasse so anzupassen, dass die verschiedenen mehrfach vorkommenden Variablen und Eigenschaftsbezeichnungen eindeutige Namen erhalten. Noch schöner wäre es allerdings, wenn wir direkt, wie auch beim Beispiel der Tabelle tblKunden, eindeutige Namen für die Klasse und die DbSet-Elemente hätten – in diesem Fall auch der besseren Lesbarkeit halber als Singular und Plural.

Erstellung des Entity Data Models anpassen

Und hier wollen wir ansetzen, indem wir die Prozedur EDMErstellen des Moduls mdlEDM aus dem Artikel Von Access zu Entity Framework: Datenmodell entsprechend anpassen. Das sieht dann ausschnittsweise wie folgt aus:

Public Sub EDMErstellen()
     ...
     For Each tdf In db.TableDefs
         If Not Left(tdf.Name, 4) = "MSys" Then
             strPK = ""
             If Not GetPrimaryKey(tdf.Name, strPK) Then
                ...
             Else
                 strEntity = Replace(strPK, "ID", "")
                 strEntities = Replace(tdf.Name, "tbl", "")
                 If strEntity = strEntities Then
                     Do While strEntity = strEntities
                         MsgBox "Plural und Singular der Bezeichnung der Entitäten (hier """ & strEntity _
                             & """) im Tabellennamen scheinen gleich zu sein." & vbCrLf & vbCrLf & "Geben Sie " _
                             & "nachfolgend eine Bezeichnung für den Singular der Entität ein und eine für den Plural."
                         strEntity = InputBox("Bezeichnung für den Singular/den Klassennamen der Entität """ _
                             & strEntity & """:", "Singular bestimmen", strEntity)
                         strEntities = InputBox("Bezeichnung für den Plural/den Namen der Auflistung der Entität """ _
                             & strEntities & """:", "Plural bestimmen", strEntities)
                     Loop
                 End If
                 ...
             End If
         End If
     Next tdf
     ...
End Sub

Nachdem wir die Bezeichnungen für die einzelne Entität und die Mehrzahl ermittelt und in strEntity und strEntities gespeichert haben, vergleichen wir diese in einer If...Then-Bedingung. Sind beide gleich, steigen wir in eine Do While-Schleifen ein. Diese zeigt zunächst eine Meldung wie in Bild 2 an.

Meldung bei Gleichheit von Singular und Plural

Bild 2: Meldung bei Gleichheit von Singular und Plural

Nach dieser Erklärung folgen zwei InputBox-Anweisungen, mit denen die Prozedur die Singular- und die Plural-Version für die zu erstellenden Klassen und DbSet-Elemente abfragt. Sind die beiden Werte von strEntity und strEntities nach dem Durchlauf der Do While-Schleife nicht mehr gleich, wird diese verlassen und die Prozedur erstellt die Klasse und die DbSet-Definition mit den neuen Namen.

Wenn wir die Prozedur nun aufrufen und die erstellten und in die Zwischenablage kopierten Code-Strukturen in das Projekt einfügen, finden wir allerdings noch einen kleinen Syntaxfehler vor: Das Fremdschlüsselfeld ArtikelID der Tabelle tblArtikel wird noch nicht entsprechend der neuen Benennung der Artikel-Entitäten umbenannt. Das Feld sollte nun ProduktID statt ArtikelID heißen und die Eigenschaft, die das Artikel-Objekt aufnimmt, sollte das Produkt-Objekt aufnehmen (siehe Bild 3).

Artikel muss noch durch Produkt ersetzt werden.

Bild 3: Artikel muss noch durch Produkt ersetzt werden.

Da wir in der Prozedur alle Tabellen in willkürlicher Reihenfolge durchlaufen, kann es sein, dass wir die Tabelle tblArtikel erst nach der Tabelle tblBestelldetails durchlaufen. Wir erfahren aber erst beim Durchlaufen der Tabelle tblArtikel, dass hier eine Umbenennung notwendig ist, die sich auch schon auf Felder der Tabelle tblBestelldetails auswirkt. Wir müssen also eine Schleife einbauen, die schon vorher prüft, ob Ersetzungen bei Tabellennamen notwendig sind, weil dieser gleichzeitig Plural und Singular ist und dann später beim zweiten Durchlauf und beim Zusammenstellen des Codes für die Entitäten und DbSet-Elemente auf die hier gewonnenen Informationen zurückgreifen.

Dazu sind einige umfangreichere Umstellungen nötig. Diese beruhen im Wesentlichen darauf, dass wir die TableDef-Objekte nicht mehr einfach in einer Schleife durchlaufen und dabei die Definitionen aller Entitäten zusammenstellen können – aus dem obigen Grund müssen wir erst einmal alle Tabellen dahingehend analysieren, ob sie Entitätsnamen hervorbringen würden, die gleichzeitig Plural und Singular sind. Also durchlaufen wir zunächst in einer Schleife alle TableDef-Objekte und tragen die relevanten Informationen jeweils in eine Klasse ein, die entsprechende Eigenschaften aufnimmt. Die neue Klasse legen Sie im VBA-Editor mit dem Menübefehl Einfügen|Klassenmodul an und speichern diese unter dem Namen clsMapping. Die folgenden privaten Variablen speichern die Werte:

Private m_Tabelle As String
Private m_PK As String
Private m_Entity As String
Private m_Entities As String
Private m_ID As String
Private m_Entity_Original As String
Private m_Entities_Original As String

Für jeden dieser Werte gibt es je eine Property Get- und eine Property Let-Methode, die wie folgt aussehen – hier stellvertretend für die Eigenschaft Tabelle:

Public Property Get Tabelle() As String
     Tabelle = m_Tabelle
End Property
Public Property Let Tabelle(str As String)
     m_Tabelle = str
End Property

Wir erstellen für jedes TableDef-Objekt eine dieser Klassen und speichern diese in einer Collection namens colMappings, die wir wie folgt deklarieren und mit dem Schlüsselwort New erstellen:

Public Sub EDMErstellen()
     Dim db As DAO.Database, tdf As DAO.TableDef, fld As DAO.Field
     Dim strDbSets As String, strPK As String, strDatatype As String
     Dim bolArray As Boolean, bolDatatypeExists As Boolean
     Dim strArray As String, strFieldname As String, strUntertyp As String
     Dim strIndex As String, strCode As String, colMappings As Collection
     Dim objMapping As clsMapping
     Dim objMappingFK As clsMapping
     Set db = CurrentDb
     Set colMappings = New Collection

Dann beginnen wir, die TableDefs zu durchlaufen. Dabei prüfen wir wieder, ob der Tabellenname nicht mit MSys oder ~ beginnt. Dann prüfen wir, ob die Tabelle einen Primärschlüssel aufweist. Falls nicht, landet ein entsprechender Kommentar in unserem in strCode zusammengestellten Code für das VB-Projekt:

     For Each tdf In db.TableDefs
         If Not Left(tdf.Name, 4) = "MSys" And Not Left(tdf.Name, 1) = "~" Then
             If Not GetPrimaryKey(tdf.Name, strPK) Then
                 strCode = strCode & "    '****" & vbCrLf
                 strCode = strCode & "    'Mehrere PKs in " & tdf.Name & ". Die Klasse wird nicht erstellt." & vbCrLf

Falls die Tabelle ein Primärschlüsselfeld besitzt, tragen wir einige Werte in die Eigenschaften des Objekts objMapping ein, das wir dazu neu erstellen. Entity wird mit dem Primärschlüsselwert ohne ID gefüllt, Entity_Original mit dem gleichen Wert. Gleiches gilt für Entities und Entities_Original, die mit dem Tabellennamen ohne tbl gefüllt werden. Sind .Entity und .Entities gleich, durchlaufen wir die schon oben beschriebene Schleife, in welcher der Benutzer solange neue Werte für .Entity und .Entities eingibt, bis sich beide unterschieden (etwa Produkt und Produkte statt Artikel). Ist das erledigt, schreiben wir noch ID in die Eigenschaft .ID, den Primärschüsselnamen in .PK und den Tabellennamen in .Tabelle:

             Else
                 Set objMapping = New clsMapping
                 With objMapping
                     .Entity = Replace(strPK, "ID", "")

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.