{"id":55000148,"date":"2018-10-01T00:00:00","date_gmt":"2020-03-27T19:34:08","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=148"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Von_Access_zu_Entity_Framework_Datenmodell","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/Von_Access_zu_Entity_Framework_Datenmodell\/","title":{"rendered":"Von Access zu Entity Framework: Datenmodell"},"content":{"rendered":"<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_05\/pic_148_001.png\" alt=\"Beispiel f&uuml;r ein zu migrierendes Datenmodell\" width=\"649,559\" height=\"299,606\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Beispiel f&uuml;r ein zu migrierendes Datenmodell<\/span><\/b><\/p>\n<p><b>Viele Leser dieses Magazins programmieren auch mit Access. Der eine oder andere hat vielleicht sogar eigene Anwendungen oder Anwendungen von Kunden auf Access-Basis, die er gern in Form eines WPF- oder ASP.NET-Projekts umsetzen w&uuml;rde. Das Problem: Der Zugriff auf die Access-Datenbank ist unter .NET nur begrenzt m&ouml;glich, die tolle Datenzugriffstechnologie Entity Framework beispielsweise unterst&uuml;tzt Access-Datenbanken nicht. Daf&uuml;r unterst&uuml;tzt es allerdings SQL Server-Datenbanken. Wie gehen wir also vor Wir migrieren die Access-Datenbanken zum SQL Server und bauen dann ein Entity Data Model auf Basis dieser Datenbank. Es geht allerdings auch anders: Sie k&ouml;nnten auch ein paar Routinen in VBA schreiben, die ein Entity Data Model direkt aus Access heraus auf Basis des gew&uuml;nschten Datenmodells erzeugen. Dieser Artikel zeigt, wie letztere M&ouml;glichkeit funktioniert.<\/b><\/p>\n<p>Als Beispiel habe ich eine kleine Bestellverwaltung zusammengestellt, welche die wesentlichen Tabellen enth&auml;lt (siehe Bild 1).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_05\/pic_148_001.png\" alt=\"Beispiel f&uuml;r ein zu migrierendes Datenmodell\" width=\"649,559\" height=\"299,606\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Beispiel f&uuml;r ein zu migrierendes Datenmodell<\/span><\/b><\/p>\n<p>Ziel ist es, aus diesem Datenmodell und den enthaltenen Daten ein Entity Data Model mit je einer Klasse f&uuml;r jede Tabelle und einer Liste der ben&ouml;tigten <b>DbSet<\/b>-Auflistungen zu erstellen.<\/p>\n<p>Au&szlig;erdem wollen wir die Befehle f&uuml;r eine <b>Seed<\/b>-Methode zusammenstellen, die notwendig ist, um die Daten aus den Access-Tabellen dann beim Initialisieren des Entity Data Models in die zu erstellende Datenbank zu schreiben. Die Grundlagen zur Initialisierung einer Datenbank finden Sie im Artikel <b>Entity Framework: Datenbankinitialisierung<\/b>.<\/p>\n<p>Was wollen wir also genau tun Wir m&ouml;chten beispielsweise f&uuml;r die Tabelle <b>tblAnreden<\/b>, deren Entwurf Sie in Bild 2 sehen, zwei Dinge erzeugen:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_05\/pic_148_002.png\" alt=\"Zu migrierende Tabelle\" width=\"499,6607\" height=\"339,8707\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Zu migrierende Tabelle<\/span><\/b><\/p>\n<ul>\n<li>die Definition einer Klasse und<\/li>\n<li>die Definition eines <b>DbSet<\/b>-Objekts.<\/li>\n<\/ul>\n<p>Die Klassendefinition soll beispielsweise wie folgt aussehen:<\/p>\n<pre><span style=\"color:blue;\">Public Class<\/span> Anrede\r\n     <span style=\"color:blue;\">Public <\/span>Property AnredeID<span style=\"color:blue;\"> As <\/span>System.Int32\r\n     <span style=\"color:blue;\">Public <\/span>Property Anrede<span style=\"color:blue;\"> As <\/span>System.String\r\n<span style=\"color:blue;\">End Class<\/span><\/pre>\n<p>Die Definition des <b>DbSet<\/b>-Objekts lautet so:<\/p>\n<pre><span style=\"color:blue;\">Public <\/span>Overridable Property Anreden()<span style=\"color:blue;\"> As <\/span>DbSet(Of Anrede)<\/pre>\n<p>Hier w&uuml;rden wir von einer automatischen Erfassung des Tabellennamens, der Felder und des Prim&auml;rschl&uuml;ssels ausgehen und von ein paar Anpassungen, um die Migration einigerma&szlig;en tauglich zu gestalten und beispielsweise nicht das Pr&auml;fix <b>tbl <\/b>mitzuschleppen. Wir sehen hier, dass die Klasse beispielsweise <b>Anrede <\/b>hei&szlig;t. Diese Bezeichnung k&ouml;nnen wir nur schwer aus dem Tabellennamen extrahieren, also holen wir ihn aus dem Namen des Prim&auml;rschl&uuml;sselfeldes (hier <b>AnredeID<\/b>), von dem wir lediglich den Zusatz <b>ID <\/b>entfernen. Die beiden Felder belassen wir im ersten Schritt bei <b>AnredeID <\/b>und <b>Anrede<\/b>. Optimaler w&auml;re beispielsweise <b>ID <\/b>und <b>Name<\/b>. Ersteres, weil wir in der objektorientierten Programmierung etwa &uuml;ber <b><Objektname>.<Feldname> <\/b>auf die Eigenschaften einer Entit&auml;t zugreifen und <b>Anrede.AnredeID <\/b>schlicht redundante Daten enth&auml;lt &#8211; <b>Anrede.ID <\/b>w&auml;re viel sch&ouml;ner. Und <b>Anrede.Anrede <\/b>ist ebenfalls optimierungsf&auml;hig. F&uuml;r die Deklaration des <b>DbSet<\/b>-Objekts verwenden wir aktuell den Namen der Tabelle ohne das Pr&auml;fix <b>tbl <\/b>und f&uuml;r den Typ der im <b>DbSet <\/b>enthaltenen Daten verwenden wir wieder den aus dem Prim&auml;rschl&uuml;sselfeld ermittelten Namen, also <b>Anrede<\/b>. Gerade bei den Bezeichnern f&uuml;r die DbSets und die Entit&auml;ten muss man darauf achten, ob sich Plural und Singular unterscheiden. Das ist beispielsweise bei <b>Artikel <\/b>schwierig (ein Artikel\/zwei Artikel), weshalb man, wenn man schon wei&szlig;, dass man mal objektorientiert mit dem Datenmodell seiner Datenbank arbeiten m&ouml;chte, besser gleich eine Bezeichnung wie <b>Produkt<\/b>\/<b>Produkte <\/b>verwendet. In unserer Beispieldatenbank verwenden wir aber weiter <b>tblArtikel<\/b>, gerade weil wir auch zeigen wollen, wie Sie solche Probleme durch Umbenennungen l&ouml;sen k&ouml;nnen.<\/p>\n<h2>Beispieldatenbank<\/h2>\n<p>Die Beispieldatenbank enth&auml;lt die im Beziehungen-Fenster von Access abgebildeten Tabellen und Beziehungen. Diese wollen wir nun, m&ouml;glichst ohne manuellen Eingriff, in die entsprechenden Entit&auml;ten und DbSets umwandeln. Dazu k&ouml;nnen wir zwei Wege gehen: Entweder wir greifen von einem Visual Studio-Projekt aus auf die Datenbank zu oder wir bauen unsere Routinen direkt in der Access-Datenbank. Mir selbst geht VBA beim Zugriff auf Tabellen, Felder, Beziehungen und so weiter immer noch viel schneller von der Hand als mit VB oder C#. Au&szlig;erdem w&auml;re der Zugriff, wie er f&uuml;r das Auslesen der Access-Tabellen n&ouml;tig w&auml;re, eine Technik, die wir prinzipiell unter .NET nicht mehr ben&ouml;tigen &#8211; hier wollen wir Datenbanksysteme wie SQL Server oder SQLite nutzen, auf die wir mit dem Entity Framework zugreifen k&ouml;nnen.<\/p>\n<p>Au&szlig;erdem d&uuml;rfte das Programmieren von Routinen, die uns aus dem Access-Datenmodell ein Entity Data Model macht, mit einer Menge Testen und &Auml;ndern verbunden sein. Das geht unter Access wesentlich schneller und komfortabler als in Visual Studio. In Access schreiben wir einfach die Prozeduren und klicken auf <b>F5<\/b>, um diese auszuf&uuml;hren, in Visual Studio m&uuml;ssten wir immer das Projekt neu starten, die Ausf&uuml;hrung pr&uuml;fen, das Projekt wieder beenden, den Code anpassen und das Ganze immer wieder von vorn beginnen. Also entscheiden wir uns in diesem Fall f&uuml;r die &auml;ltere, aber pragmatischere Vorgehensweise.<\/p>\n<h2>.NET-Projekt zum Testen vorbereiten<\/h2>\n<p>Damit wir die gleich unter Access generierten Klassen im .NET-Projekt auf ihre Tauglichkeit pr&uuml;fen k&ouml;nnen, legen wir gleich ein neues Projekt des Typs <b>VB|WPF-App <\/b>mit dem Namen <b>AccessZuEF <\/b>an. Diesem f&uuml;gen wir ein neues Objekt des Typs <b>ADO.NET Entity Data Model <\/b>namens <b>BestellverwaltungContext <\/b>mit dem Typ <b>Leeres Code First-Modell <\/b>hinzu. Dies legt automatisch die Klasse <b>BestellverwaltungContext.vb <\/b>f&uuml;r uns an, der wir dann gleich unsere Liste der <b>DbSet<\/b>-Elemente hinzuf&uuml;gen k&ouml;nnen. Der Einfachheit halber packen wir unsere <b>Class<\/b>-Elemente zum Testen auch erst einmal hier herein.<\/p>\n<h2>Zugriff auf die Tabellen, Felder und Beziehungen<\/h2>\n<p>Den Zugriff auf die f&uuml;r uns interessanten Elemente gestalten wir mit der DAO-Bibliothek und VBA. Wir arbeiten uns nun Schritt f&uuml;r Schritt an die Umwandlung des Datenmodells und der enthaltenen Daten an die entsprechenden .NET-Klassen heran. Zuerst sehen wir uns ein paar Hilfsfunktionen an, die uns auf dem Weg behilflich sind.<\/p>\n<h2>Datentypen umwandeln<\/h2>\n<p>Unter dem Link <b>https:\/\/support.microsoft.com\/de-de\/help\/320435\/info-oledbtype-enumeration-vs-microsoft-access-data-types <\/b>finden wir eine Liste der Access-Datentypen und der entsprechenden Datentypen unter .NET. Daraus bauen wir uns eine kleine Funktion, die den Access-Datentyp als Parameter erwartet und den .NET-Datentyp als Ergebnis zur&uuml;ckliefert. Diese Funktion sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>DataTypeNET(intDatatype<span style=\"color:blue;\"> As <\/span>DataTypeEnum, strDatatype<span style=\"color:blue;\"> As String<\/span>, bolArray<span style=\"color:blue;\"> As Boolean<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolDatatype<span style=\"color:blue;\"> As Boolean<\/span>\r\n     bolDatatype = <span style=\"color:blue;\">True<\/span>\r\n     bolArray = <span style=\"color:blue;\">False<\/span>\r\n     Select Case intDatatype\r\n         <span style=\"color:blue;\">Case <\/span>dbText: strDatatype = \"System.String\"\r\n         <span style=\"color:blue;\">Case <\/span>dbMemo: strDatatype = \"System.String\"\r\n         <span style=\"color:blue;\">Case <\/span>dbByte: strDatatype = \"System.Byte\"\r\n         <span style=\"color:blue;\">Case <\/span>dbBoolean: strDatatype = \"System.Boolean\"\r\n         <span style=\"color:blue;\">Case <\/span>dbDate: strDatatype = \"System.DateTime\"\r\n         <span style=\"color:blue;\">Case <\/span>dbCurrency: strDatatype = \"System.Decimal\"\r\n         <span style=\"color:blue;\">Case <\/span>dbDecimal: strDatatype = \"System.Decimal\"\r\n         <span style=\"color:blue;\">Case <\/span>dbDouble: strDatatype = \"System.Double\"\r\n         <span style=\"color:blue;\">Case <\/span>dbGUID: strDatatype = \"System.Guid\"\r\n         <span style=\"color:blue;\">Case <\/span>dbLong: strDatatype = \"System.Int32\"\r\n         <span style=\"color:blue;\">Case <\/span>dbLongBinary\r\n             strDatatype = \"System.Byte\"\r\n             bolArray = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">Case <\/span>dbSingle: strDatatype = \"System.Single\"\r\n         <span style=\"color:blue;\">Case <\/span>dbInteger: strDatatype = \"System.Int16\"\r\n         <span style=\"color:blue;\">Case <\/span>dbBinary\r\n             strDatatype = \"System.Byte\"\r\n             bolArray = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">Case Else<\/span>\r\n             bolDatatype = <span style=\"color:blue;\">False<\/span>\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n     DataTypeNET = bolDatatype\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die Funktion erwartet drei Parameter, von denen nur der erste Parameter Daten an die Funktion schickt &#8211; die &uuml;brigen beiden liefern die R&uuml;ckgabewerte. <b>intDataType <\/b>erwartet die Konstante beziehungsweise den entsprechenden Zahlenwert, den wir aus der Eigenschaft <b>Type <\/b>eines <b>Field<\/b>-Elements erhalten. <b>strDatatype <\/b>liefert den .NET-Datentyp als String zur&uuml;ck. Manche Datentypen werden in .NET als Byte-Array gestaltet. Wenn der Datentyp ein Array erfordert, markieren wir den dritten Parameter <b>bolArray <\/b>als <b>True<\/b>. Es kann auch sein, dass der Datentyp nicht vorhanden ist, was schlicht daran liegt, dass er in dieser Funktion nicht ber&uuml;cksichtigt wird &#8211; in diesem Fall kommt der eigentliche R&uuml;ckgabewert der Funktion zum Einsatz, der den Datentyp <b>Boolean <\/b>hat.<\/p>\n<p>Die Funktion stellt die Variable <b>bolDatatype<\/b>, die festlegt, ob die Funktion einen passenden Datentyp in .NET gefunden hat, standardm&auml;&szlig;ig auf <b>True <\/b>ein. Diese wird erst auf <b>False <\/b>eingestellt, wenn der Datentyp in der folgenden <b>Select Case<\/b>-Bedingung nicht gefunden wurde. Die Variable <b>bolArray <\/b>wird zuerst auf <b>False <\/b>eingestellt und erh&auml;lt erst den Wert <b>True<\/b>, wenn der Datentyp ein Array ben&ouml;tigt. Das ist in diesem Fall n&ouml;tig, da <b>bolArray <\/b>ja auch in der aufrufenden Prozedur verwendet wird und gegebenenfalls vom vorherigen Aufruf noch den Wert <b>True <\/b>enth&auml;lt. Davon ab vergleicht die <b>Select Case<\/b>-Bedingung den mit <b>intDatatype <\/b>&uuml;bergebenen Wert mit Konstanten wie <b>dbText<\/b>, <b>dbMemo<\/b>, <b>dbByte <\/b>und so weiter und tr&auml;gt, wenn sie einen passenen Eintrag gefunden hat, den entsprechenden .NET-Datentyp in die Variable <b>strDatatype <\/b>ein. Am Ende wird schlie&szlig;lich der Wert von <b>bolDatatype <\/b>noch als R&uuml;ckgabewert der Funktion festgelegt.<\/p>\n<h2>Prim&auml;rschl&uuml;sselfeld ermitteln<\/h2>\n<p>Wir gehen davon aus, dass die Prim&auml;rschl&uuml;sselfelder der Tabellen mit einer Bezeichnung versehen sind, die aus dem Singular der Bezeichnung besteht, die sich im Tabellennamen befindet, und der angeh&auml;ngten Zeichenkette <b>ID<\/b> &#8211; bei <b>tblAnreden <\/b>also etwa <b>AnredeID<\/b> (das ist zumindest die Konvention, die ich seit 20 Jahren in Magazin <b>Access im Unternehmen <\/b>und in all meinen anderen Ver&ouml;ffentlichungen &uuml;ber Access verwende). Um den Namen herauszubekommen, m&uuml;ssen wir nun allerdings noch zuverl&auml;ssig bestimmen k&ouml;nnen, welches Feld das Prim&auml;rschl&uuml;sselfeld ist. Wir gehen an dieser Stelle davon aus, dass jede Tabelle auch nur einen aus einem Feld bestehenden Prim&auml;rschl&uuml;ssel verwendet.<\/p>\n<p>Die folgende Funktion namens <b>GetPrimaryKeys <\/b>ermittelt dennoch die Namen aller Prim&auml;rschl&uuml;sselfelder einer Tabelle. Dazu erh&auml;lt sie mit <b>strTable <\/b>den Tabellennamen als Parameter sowie <b>strPKs <\/b>als R&uuml;ckgabeparameter. Die Funktion l&auml;dt ein <b>TableDef<\/b>-Objekt auf Basis des Namens der Tabelle und durchl&auml;uft dann alle <b>Index<\/b>-Elemente dieser Tabelle in einer <b>For Each<\/b>-Schleife. Wenn es sich bei dem Index um einen Prim&auml;rschl&uuml;ssel handelt (<b>Primary = True<\/b>), durchl&auml;uft die Funktion alle Felder des Indexes und f&uuml;gt die Namen der Felder in der Variablen <b>strPKTemp <\/b>durch Semikola getrennt aneinander. Das Ergebnis wird mit dem Parameter <b>strPKs <\/b>zur&uuml;ckgeschickt. Der R&uuml;ckgabewert der Funktion <b>GetPrimaryKeys <\/b>wird mit dem Wert <b>True <\/b>gef&uuml;llt, wenn es mindestens ein Prim&auml;rschl&uuml;sselfeld gibt.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetPrimaryKeys(strTable<span style=\"color:blue;\"> As String<\/span>, strPKs<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n      <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n      <span style=\"color:blue;\">Dim <\/span>tdf<span style=\"color:blue;\"> As <\/span>DAO.TableDef\r\n      <span style=\"color:blue;\">Dim <\/span>idx<span style=\"color:blue;\"> As <\/span>DAO.Index\r\n      <span style=\"color:blue;\">Dim <\/span>strPKTemp<span style=\"color:blue;\"> As String<\/span>\r\n      <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Integer<\/span>\r\n      <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n      <span style=\"color:blue;\">Set<\/span> tdf = db.TableDefs(strTable)\r\n      For Each idx In tdf.Indexes\r\n          <span style=\"color:blue;\">If <\/span>idx.Primary<span style=\"color:blue;\"> Then<\/span>\r\n              For i = 0 To idx.Fields.Count - 1\r\n                  strPKTemp = strPKTemp & idx.Fields(0).Name & \";\"\r\n              <span style=\"color:blue;\">Next<\/span> i\r\n          <span style=\"color:blue;\">End If<\/span>\r\n      <span style=\"color:blue;\">Next<\/span> idx\r\n      <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strPKTemp) = 0<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">Exit Function<\/span>\r\n      <span style=\"color:blue;\">End If<\/span>\r\n      strPKs = <span style=\"color:blue;\">Left<\/span>(strPKTemp, <span style=\"color:blue;\">Len<\/span>(strPKTemp) - 1)\r\n      GetPrimaryKeys = <span style=\"color:blue;\">True<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die folgende Funktion <b>GetPrimaryKey <\/b>unterscheidet sich im Namen nur durch den Singular gegen&uuml;ber dem Plural von der Funktion <b>GetPrimaryKey<\/b>. Was macht diese Funktion Sie ruft in erster Linie die zuvor beschriebene Funktion <b>GetPrimaryKeys <\/b>auf und pr&uuml;ft, ob die mit dem Parameter <b>strTable <\/b>&uuml;bergebene Tabelle nur um einen oder mehrere Prim&auml;rschl&uuml;ssel enth&auml;lt. Falls es mehrere sind, wird mit <b>strPK <\/b>eine leere Zeichenkette und mit dem R&uuml;ckgabewert der Wert <b>False <\/b>zur&uuml;ckgegeben. Falls es sich um ein einziges Prim&auml;rschl&uuml;sselfeld handelt, wird mit <b>strPK<\/b> der Name des Prim&auml;rschl&uuml;sselfeldes sowie mit dem R&uuml;ckgabewert der Wert <b>True <\/b>zur&uuml;ckgeliefert:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetPrimaryKey(strTable<span style=\"color:blue;\"> As String<\/span>, strPK<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strPKs<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">If <\/span>GetPrimaryKeys(strTable, strPKs) = <span style=\"color:blue;\">True<\/span><span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">InStr<\/span>(1, strPKs, \";\")<span style=\"color:blue;\"> Then<\/span>\r\n             <span style=\"color:blue;\">Exit Function<\/span>\r\n         <span style=\"color:blue;\">Else<\/span>\r\n             strPK = strPKs\r\n             GetPrimaryKey = <span style=\"color:blue;\">True<\/span>\r\n         <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">Exit Function<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>In der aufrufenden Prozedur, die wir uns gleich im Anschluss ansehen, k&ouml;nnen wir dann aufgrund der R&uuml;ckgabewerte entweder die Definition der Klasse und der <b>DbSet<\/b>-Anweisung zusammenstellen oder eine Meldung ausgeben, dass die Bedingungen f&uuml;r das Erstellen der Elemente wegen keinem Prim&auml;rschl&uuml;sselfeld oder mehr als einem Prim&auml;rschl&uuml;sselfeld nicht gegeben sind.<\/p>\n<h2>Klassen und DbSet-Deklarationen zusammenstellen<\/h2>\n<p>Die folgende Prozedur erledigt den eigentlichen Teil der Arbeit: Sie nutzt die Ergebnisse der zuvor beschriebenen Funktionen und weitere Daten, um die Klassendefinitionen und die <b>DbSet<\/b>-Deklarationen zusammenzustellen. Im ersten Schritt wollen wir diese noch mit <b>Debug.Print <\/b>im Direktbereich ausgeben. Sp&auml;ter werden wir direkt entsprechende Dateien daraus erzeugen, die Sie nur noch aus dem Windows-Explorer in den Projektmappen-Explorer des Ziel-Projekts in Visual Studio ziehen. Die Prozedur hei&szlig;t <b>EDMErstellen <\/b>und durchl&auml;uft nach dem Deklarationsteil zun&auml;chst einmal alle <b>TableDef<\/b>-Elemente, also alle Tabellendefinitionen, der mit <b>CurrentDb <\/b>referenzierten Datenbank &#8211; also der aktuell ge&ouml;ffneten Datenbank:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>EDMErstellen()\r\n     <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n     <span style=\"color:blue;\">Dim <\/span>tdf<span style=\"color:blue;\"> As <\/span>DAO.TableDef\r\n     <span style=\"color:blue;\">Dim <\/span>fld<span style=\"color:blue;\"> As <\/span>DAO.Field\r\n     <span style=\"color:blue;\">Dim <\/span>strDbSets<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strEntity<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strPK<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strDatatype<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolArray<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strEntities<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>bolDatatypeExists<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strArray<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n     For Each tdf In db.TableDefs<\/pre>\n<div class=\"rcp_restricted\"><p><span style=\"color: #ff0000;\">M&ouml;chten Sie weiterlesen? Dann l&ouml;sen Sie Ihr Ticket!<\/span><br \/>\n<span style=\"color: #ff0000;\">Hier geht es zur Bestellung des Jahresabonnements des Magazins <strong>Visual Basic Entwickler<\/strong>:<\/span><br \/>\n<span style=\"color: #ff0000;\"><a style=\"color: #ff0000;\" href=\"https:\/\/shop.minhorst.com\/magazine\/363\/visual-basic-entwickler-jahresabonnement?c=77\">Zur Bestellung ...<\/a><\/span><br \/>\n<span style=\"color: #ff0000;\">Danach greifen Sie sofort auf <strong>alle rund 200 Artikel<\/strong> unseres Angebots zu - auch auf diesen hier!<\/span><br \/>\n<span style=\"color: #000000;\">Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:<\/span><\/p>\n<\/div>\n\n\t\n\t<form id=\"rcp_login_form\"  class=\"rcp_form\" method=\"POST\" action=\"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000148\/\">\n\n\t\t\n\t\t<fieldset class=\"rcp_login_data\">\n\t\t\t<p>\n\t\t\t\t<label for=\"rcp_user_login\">Username or Email<\/label>\n\t\t\t\t<input name=\"rcp_user_login\" id=\"rcp_user_login\" class=\"required\" type=\"text\"\/>\n\t\t\t<\/p>\n\t\t\t<p>\n\t\t\t\t<label for=\"rcp_user_pass\">Password<\/label>\n\t\t\t\t<input name=\"rcp_user_pass\" id=\"rcp_user_pass\" class=\"required\" type=\"password\"\/>\n\t\t\t<\/p>\n\t\t\t\t\t\t<p>\n\t\t\t\t<input type=\"checkbox\" name=\"rcp_user_remember\" id=\"rcp_user_remember\" value=\"1\"\/>\n\t\t\t\t<label for=\"rcp_user_remember\">Remember me<\/label>\n\t\t\t<\/p>\n\t\t\t<p class=\"rcp_lost_password\"><a href=\"\/data\/wp\/v2\/posts\/55000148?rcp_action=lostpassword\"><\/a><\/p>\n\t\t\t<p>\n\t\t\t\t<input type=\"hidden\" name=\"rcp_action\" value=\"login\"\/>\n\t\t\t\t<input type=\"hidden\" name=\"rcp_redirect\" value=\"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000148\/\"\/>\n\t\t\t\t<input type=\"hidden\" name=\"rcp_login_nonce\" value=\"123b777de9\"\/>\n\t\t\t\t<input id=\"rcp_login_submit\" class=\"rcp-button\" type=\"submit\" value=\"Login\"\/>\n\t\t\t<\/p>\n\t\t\t\t\t<\/fieldset>\n\n\t\t\n\t<\/form>\n<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Viele Leser dieses Magazins programmieren auch mit Access. Der eine oder andere hat vielleicht sogar eigene Anwendungen oder Anwendungen von Kunden auf Access-Basis, die er gern in Form eines WPF- oder ASP.NET-Projekts umsetzen w&uuml;rde. Das Problem: Der Zugriff auf die Access-Datenbank ist unter .NET nur begrenzt m&ouml;glich, die tolle Datenzugriffstechnologie Entity Framework beispielsweise unterst&uuml;tzt Access-Datenbanken nicht. Daf&uuml;r unterst&uuml;tzt es allerdings SQL Server-Datenbanken. Wie gehen wir also vor Wir migrieren die Access-Datenbanken zum SQL Server und bauen dann ein Entity Data Model auf Basis dieser Datenbank. Es geht allerdings auch anders: Sie k&ouml;nnten auch ein paar Routinen in VBA schreiben, die ein Entity Data Model direkt aus Access heraus auf Basis des gew&uuml;nschten Datenmodells erzeugen. Dieser Artikel zeigt, wie letztere M&ouml;glichkeit funktioniert.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662018,66052018,44000021,44000022],"tags":[],"yst_prominent_words":[],"class_list":["post-55000148","post","type-post","status-publish","format-standard","hentry","category-662018","category-66052018","category-Entity_Framework","category-Von_Access_zu_NET"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000148","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000148"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000148\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000148"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000148"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000148"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000148"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}