Von Access zu Entity Framework: Daten

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: Daten

Viele Leser dieses Magazins programmieren auch mit Access. Daher haben wir im Artikel »Von Access zu Entity Framework: Datenmodell« bereits gezeigt, wie Sie die meisten Elemente eines Datenmodells in Klassen für ein Entity Data Model überführen, die Sie dann wiederum zum Erstellen einer SQL Server-Datenbank per Code First nutzen können. Was fehlt, sind allerdings noch die Daten in diesen Tabellen. Wie Sie den Code erstellen, um auch die Daten über eine entsprechend Seed-Methode in die Datenbank zu schreiben, erfahren Sie in diesem Artikel.

Im Artikel Von Access zu Entity Framework: Datenmodell haben wir uns darum gekümmert, das Datenmodell unserer kleinen Access-Beispielanwendung aus Bild 1 zuerst in die Klassen eines Entity Data Models zu überführen. Dann haben wir mit den Methoden Add-Migration und Update-Database in der Paket-Manager-Konsole dafür gesorgt, dass diese Klassen zusammen mit den passenden DbSet-Auflistungen in eine neue SQL Server-Datenbank überführt wurden. Die dort vorgestellten Routinen sind sicher noch nicht perfekt, aber sie sind eine gute Grundlage, die Sie selbst erweitern können – oder Sie teilen uns einfach mit, bei welchen Eigenarten von Tabellendefinitionen oder Datenmodellen die Erstellung des Entity Data Models oder der SQL Server-Datenbank noch nicht funktioniert.

Beispiel für ein zu migrierendes Datenmodell

Bild 1: Beispiel für ein zu migrierendes Datenmodell

Im vorliegenden Artikel nun wollen wir die Daten in diesen Tabellen in eine Form bringen, in der wie diese beim Erstellen der Datenbank von Visual Studio aus mit der Seed-Methode direkt in die frisch angelegten Tabellen schreiben können. Die Grundlagen dafür erhalten Sie im Artikel Entity Framework: Tabellen füllen sowie in den beiden Artikeln Entity Framework: Datenbankinitialisierung und Entity Framework: Datenbankmigration.

Anlegen der Daten für eine einzelne Tabelle ohne Fremdschlüsselfelder

Wir wollen uns langsam an die Lösung der Aufgabe heranbewegen und uns zuerst einmal eine einfache Tabelle heraussuchen, deren Daten wir dann über den Weg der Seed-Methode etwa der Configuration-Klasse im Migrations-Ordner nach dem Erstellen des Datenmodells in die Datenbank schreiben (wie Sie diesen Ordner erstellen, erfahren Sie im Artikel Entity Framework: Datenbankmigration).

Die Tabelle tblAnreden unserer Ausgangsdatenbank enthält nur zwei Datensätze (siehe Bild 2). Diese wollen wir nun in einem ersten Schritt in Anweisungen schreiben, die wir in die Seed-Methode der .NET-Anwendung schreiben können. Wir schauen uns erst einmal an, wie die fertige Seed-Methode anschließend aussehen soll:

Die Tabelle tblAnreden der Ausgangsdatenbank

Bild 2: Die Tabelle tblAnreden der Ausgangsdatenbank

Namespace Migrations
     Friend NotInheritable Class Configuration 
         Inherits DbMigrationsConfiguration(Of BestellverwaltungContext)
         Public Sub New()
             AutomaticMigrationsEnabled = False
         End Sub
         Protected Overrides Sub Seed(context As BestellverwaltungContext)
             context.Anreden.AddOrUpdate(Function(x) x.ID,
                 New Anrede() With {.Name = "Herr"},
                 New Anrede() With {.Name = "Frau"}
             )
         End Sub
     End Class
End Namespace

Wir sollten uns überlegen, ob wir per VBA immer die komplette Klasse namens Configuration wie oben abgebildet erstellen sollen oder nur die Seed-Methode in dieser Klasse. Praktischer ist es vermutlich, gleich die komplette Klasse zu erstellen, damit man entweder – im ersten Schritt – die Ausgabe dieser Klasse aus dem Direktbereich des VBA-Editors von Access komplett kopieren und als Ganzes in diese Klasse einsetzen kann. Im zweiten Schritt könnten wir den Code direkt in die entsprechende Datei schreiben, die dann von Visual Studio aktualisiert eingelesen wird.

Die Seed-Methode enthält hier eine Anweisung, bei der wir die AddOrUpdate-Methode der Auflistung Anreden aufrufen und dieser eine Funktion übergeben, welche jeweils ein neues Objekt auf Basis der Klasse Anrede enthält. Für diese legen wir dann jeweils die Anrede fest.

Wir bauen zunächst eine Prozedur, welche den Rahmen im Direktfenster ausgibt, also die Imports-Anweisungen, den Namespace, die Klasse und die enthaltenen Methoden – mit Ausnahme der Seed-Methode:

Public Sub SeedErstellen()
     Dim strSeed As String
     strSeed = strSeed & "Imports System" & vbCrLf
     strSeed = strSeed & "Imports System.Data.Entity"
     strSeed = strSeed & "Imports System.Data.Entity.Migrations"
     strSeed = strSeed & "Imports System.Linq"
     strSeed = strSeed & ""
     strSeed = strSeed & "Namespace Migrations"
     strSeed = strSeed & " Friend NotInheritable Class Configuration"
     strSeed = strSeed & "        Inherits DbMigrationsConfiguration(Of BestellverwaltungContext)"
     strSeed = strSeed & "        Public Sub New()"
     strSeed = strSeed & "            AutomaticMigrationsEnabled = False"
     strSeed = strSeed & "        End Sub"
     strSeed = strSeed & "        Protected Overrides Sub Seed(context As BestellverwaltungContext)"
     strSeed = strSeed & SeedData
     strSeed = strSeed & "        End Sub"
     strSeed = strSeed & "    End Class"
     strSeed = strSeed & "End Namespace"
End Sub

Das ist noch kein Hexenwerk. Interessant ist die hier eingebettete Prozedur SeedData. Diese stellt die Anweisungen zusammen, die wir zur Methode Seed hinzufügen wollen. Statisch sieht das für unsere Tabelle tblAnreden zunächst wie folgt aus:

Public Sub SeedData()
     Dim strSeedData As String
     strSeedData = strSeedData & "            context.Anreden.AddOrUpdate(Function(x) x.ID,"
     strSeedData = strSeedData & "                New Anrede() With {.Name = ""Herr""},"
     strSeedData = strSeedData & "                New Anrede() With {.Name = ""Frau""}"
     strSeedData = strSeedData & "            )"
     SeedData = strSeedData
End Sub

Um dies aus VBA heraus dynamisch für die Tabelle tblAnreden zu erzeugen, müssen wir dies allerdings mit einigen erst zur Laufzeit ermittelten Daten füllen. Das erledigen wir in den folgenden Schritten.

Seed für mehrere Tabellen

Die Prozedur SeedData wandeln wir in eine Funktion um, die das Ergebnis an die aufrufende Prozedur zurückgeben soll. Sie enthält dafür für jede Tabelle der Datenbank einen Aufruf der Tabelle SeedData und fügt das Ergebnis jeweils an die Zeichenkette strSeed an. Die Aufrufe der Tabellen erledigen wir in einer For Each-Schleife über alle TableDef-Elemente der mit CurrentDb referenzierten aktuellen Datenbank. In der Schleife prüfen wir zunächst, ob es sich bei der aktuellen Tabelle nicht um eine Systemtabelle von Access handelt. Nur wenn das nicht der Fall ist, rufen wir die Funktion SeedData mit dem Namen der Tabelle auf. Den Namen der Tabelle ermitteln wir dabei mit der Eigenschaft Name des TableDef-Objekts.

Public Sub SeedErstellen()
     ...
     strSeed = strSeed & " Protected Overrides Sub Seed(context As BestellverwaltungContext)" & vbCrLf
     For Each tdf In db.TableDefs
         If Not Left(tdf.Name, 4) = "MSYS" Then
             strSeed = strSeed & SeedData(tdf.Name) & vbCrLf
         End If
     Next tdf    strSeed = strSeed & "        End Sub" & vbCrLf
     strSeed = strSeed & "    End Class" & vbCrLf
     strSeed = strSeed & "End Namespace"
     Inzwischenablage strSeed
End Sub

Am Ende der Prozedur nutzen wir die Prozedur InZwischenablage, um den Inhalt der Variablen strSeed, in der wir die Anweisungen für die Configuration.vb-Klasse gesammelt haben, in die Zwischenablage zu kopieren. Das ist eine bessere Lösung als die Ausgabe im Direktbereich, weil dieser nur eine begrenzte Anzahl Zeichen aufnehmen kann und bei vielen Daten der obere Teil der Ausgabe dann nicht mehr verfügbar ist. Die Prozedur InZwischenablage finden Sie im Modul mdlZwischenablage.

Die Funktion SeedData zum Zusammenstellen der AddOrUpdate-Aufrufe

Die Funktion SeedData bohren wir gegenüber der vorherigen Version erheblich auf. Als Erstes fällt der Parameter strTabelle auf, welcher die Information enthält, für die Datensätze welcher Tabelle die AddOrUpdate-Methoden zusammengestellt werden sollen.

Public Function SeedData(strTabelle As String) As String
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim strSeedData As String
     Dim strPK As String
     Dim strFelder As String
     Dim fld As DAO.Field
     Dim strEntity As String
     Dim strEntities As String
     Dim strProperty As String

Nach der Deklaration der Variablen speichern wir einen Verweis auf die aktuelle Datenbank in der Variablen db. Dann prüfen wir mit der Funktion GetPrimaryKey, die wir bereits im Artikel Von Access zu Entity Framework: Datenmodell erläutert haben, ob die gerade untersuchte Tabelle genau ein Primärschlüsselfeld bereitstellt. Ist das der Fall, wird eine entsprechende Meldung im Direktbereich ausgeben und die Funktion wird mit Exit Function verlassen:

     Set db = CurrentDb
     If Not GetPrimaryKey(strTabelle, strPK) Then
         Debug.Print "    'Mehrere PKs in " & strTabelle & ". Die Klasse wird nicht erstellt."
         Exit Function

Kann jedoch genau ein Primärschlüsselfeld gefunden werden, landet dies in der Variablen strPK. Daraus ermitteln wir zunächst den Namen der Entität, indem wir hinten die Zeichenkette ID entfernen. Außerdem ermittelten wir den Namen der Tabelle, indem wir vom Tabellenname vorn tbl abtrennen und das Ergebnis in der Variablen strEntitaeten speichern:

     Else
         strEntity = Replace(strPK, "ID", "")
         strEntities = Replace(strTabelle, "tbl", "")

Damit starten wir in die Ermittlung der benötigten Daten, indem wir ein Recordset auf Basis der mit strTabelle übergebenen Tabelle erstellen. Außerdem tragen wir die erste Zeile in die Variable strSeedData ein, welche zum Beispiel bei der Tabelle tblAnreden nun context.Anreden.AddOrUpdate(Function(x) x.ID, lautet:

         Set rst = db.OpenRecordset("SELECT * FROM " & strTabelle, dbOpenDynaset)
         strSeedData = strSeedData & "            context." & strEntities & ".AddOrUpdate(Function(x) x.ID," & vbCrLf

Danach durchlaufen wir in einer Do While-Schleife alle Datensätze der aktuell bearbeiteten Tabelle. Im ersten Schritt legen wir die Zeile New Anrede() With { an:

         Do While Not rst.EOF
             strSeedData = strSeedData & "                New " & strEntity & "() With {"

Dann leeren wir die Variable strFelder, die wir anschließend mit den Zuweisungen für die einzelnen Felder mit den Werten des Recordsets füllen:

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.