{"id":55000453,"date":"2007-04-01T00:00:00","date_gmt":"2020-05-15T16:27:29","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=453"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Mehrschichtige_Anwendungen","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/","title":{"rendered":"Mehrschichtige Anwendungen"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg02.met.vgwort.de\/na\/5c35e9e8ff794e26ab4ad06ea049af58\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Access ist &#8211; rein architektonisch betrachtet &#8211; auf das Anlegen monolithischer Anwendungen mit Objekten mit starker Abh&auml;ngigkeit ausgelegt. Formulare und Berichte sind starr mit den zu Grunde liegenden Datenherk&uuml;nften verbunden und der Code liegt jeweils im passenden Formular- oder Berichtsmodul oder, falls n&ouml;tig, in einem Standard- oder Klassenmodul. Wie man auch mit Access mehrschichtige Anwendungen baut, zeigt dieser Beitrag.<\/b><\/p>\n<p>Im Gegensatz zu objektorientierten Programmiersprachen gibt es in Access-Anwendungen keine einzelnen Dateien, die unterschiedliche Klassen repr&auml;sentieren, sondern nur eine einzige Datei mit der Endung .mdb, die alle Objekte enth&auml;lt.<\/p>\n<p>Wenn Sie hier eine mehrschichtige Anwendung bauen m&ouml;chten und dies von richtigen objektorientierten Programmiersprachen gewohnt sind, m&uuml;ssen Sie von dem Gedanken Abschied nehmen, jede Klasse fein s&auml;uberlich in einer eigenen Datei zu speichern.<\/p>\n<p>Davon abgesehen funktioniert das Programmieren mehrschichtiger Anwendungen aber genauso wie in anderen objektorientierten Programmiersprachen &#8211; nur dass Sie auf Eigenschaften wie Vererbung und Polymorphie verzichten m&uuml;ssen und alle Klassen in der .mdb-Datei enthalten sind.<\/p>\n<p>Ein Grund, eine mehrschichtige Anwendung zu erstellen, ist die starke Verzahnung von Benutzeroberfl&auml;che und Anwendungslogik. Das gilt vor allem f&uuml;r den in Formularen und Berichten enthaltenen Code, der nicht nur die Benutzeroberfl&auml;che steuert, sondern auch die Businesslogik und die Funktionen f&uuml;r den Datenzugriff enth&auml;lt. Letztere sind dabei noch tiefer verborgen, n&auml;mlich in den passenden Formular-, Berichts- und Steuerelementeigenschaften, die f&uuml;r die Bindung an die entsprechenden Tabellen und Abfragen sorgen. Teilweise befindet sich die Businesslogik sogar direkt in den Tabellen &#8211; etwa in Form der G&uuml;ltigkeitsregeln und der &uuml;brigen Integrit&auml;tsmechanismen wie der referentiellen Integrit&auml;t und der darin enthaltenen L&ouml;sch- und Aktualisierungsweitergabe.<\/p>\n<p>Wegen dieser Aufteilung kann die Pflege solcher Anwendungen sehr zeitintensiv werden: Wenn Sie etwa eine Meldung, die auf einen falschen Datentyp bei der Eingabe in ein Formularfeld hinweist, &auml;ndern oder entfernen m&ouml;chten, sind Sie unter Umst&auml;nden lange unterwegs, da sich der Ausl&ouml;ser in verschiedenen Ereignissen des Formulars oder auch im Tabellenentwurf befinden kann.<\/p>\n<p>Ganz klar: Wer eine konsistente Vorgehensweise bei der Programmierung von Anwendungen an den Tag legt, der wird auch genau wissen, wo in seiner Anwendung sich welche Funktionen befinden. Aber erstens kann es ja auch sein, dass sich mal jemand anders um die Wartung oder Weiterentwicklung der Anwendung k&uuml;mmern muss, und zweitens entwickeln Sie sich selbst ja auch weiter und erkennen nach einer gewissen Zeit die funktionale Struktur fr&uuml;herer Anwendungen nicht mehr wieder. Und Access ist nat&uuml;rlich zun&auml;chst einmal daf&uuml;r ausgelegt, auf schnellstem Wege Anwendungen f&uuml;r den Zugriff auf und die Verwaltung von Daten zu entwickeln. Wenn eine Anwendung allerdings ein gewisses Ma&szlig; an Komplexit&auml;t &uuml;berschritten hat und man f&uuml;r kleine &Atilde;&#8220;nderungen beinahe genauso lange braucht, als wenn man die halbe Anwendung neu programmieren w&uuml;rde, sollte man &uuml;ber alternative Vorgehensweisen nachdenken.<\/p>\n<p>Diese liegen beispielsweise in der Verwendung eines mehrschichtigen Datenmodells. Solche Modelle gibt es in mehreren Varianten mit unterschiedlicher Interpretation.<\/p>\n<p>Im Folgenden lernen Sie ein Modell kennen, das je nach Sichtweise aus drei oder vier Schichten besteht: Der Benutzeroberfl&auml;che (GUI-Schicht), der Business-Schicht, der Datenzugriffsschicht und den Daten. Manch einer betrachtet die Datenzugriffsschicht und die Daten als Einheit, andere sehen zwei Schichten darin. Im Rahmen dieses Beitrags werden Datenzugriffsschicht und Daten als zwei Schichten betrachtet.<\/p>\n<h2>Beispiel f&uuml;r den mehrschichtigen Datenzugriff<\/h2>\n<p>Als Beispiel f&uuml;r den mehrschichtigen Datenzugriff dienen eine Tabelle namens <b>tblPersonal <\/b>(siehe Bild 1) und ein Formular namens <b>frmPersonal <\/b>(siehe Bild 2). Dabei verwenden wir aus Gr&uuml;nden der &Uuml;bersicht nur einige der in der Tabelle enthaltenen Felder.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_02\/MehrschichtigeAnwendung-web-images\/pic001_opt.jpeg\" alt=\"pic001.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 1: Diese Tabelle dient als Datenherkunft des Formulars.<\/span><\/b><\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_02\/MehrschichtigeAnwendung-web-images\/pic002_opt.jpeg\" alt=\"pic002.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 2: Das Formular zum Anzeigen der Daten ist ungebunden.<\/span><\/b><\/p>\n<p>Das Formular ist komplett ungebunden und enth&auml;lt die folgenden Steuerelemente:<\/p>\n<ul>\n<li class=\"aufz-hlung\"><b>cboSchnellsuche<\/b>: dient der Auswahl von Mitarbeitern<\/li>\n<li class=\"aufz-hlung\"><b>txtPersonalID<\/b>: schreibgesch&uuml;tzt<\/li>\n<li class=\"aufz-hlung\"><b>txtVorname<\/b>, <b>txtNachname<\/b>, <b>txtPosition<\/b>, <b>txtAnrede<\/b>, <b>txtEinstellung<\/b>: Textfelder der Tabelle<\/li>\n<li class=\"aufz-hlung\"><b>cmdOK<\/b>: Schaltfl&auml;che zum Schlie&szlig;en des Formulars und zum Speichern des aktuellen Inhalts<\/li>\n<li class=\"aufz-hlung\"><b>cmdLoeschen<\/b>: Schaltfl&auml;che zum L&ouml;schen des aktuellen Datensatzes<\/li>\n<li class=\"aufz-hlung\"><b>cmdNeu<\/b>: Schaltfl&auml;che zum Anlegen eines neuen Datensatzes<\/li>\n<\/ul>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Die GUI-Schicht<\/p>\n<p>Die GUI-Schicht &#8211; also die Benutzeroberfl&auml;che &#8211; bildet f&uuml;r das nachfolgende Beispiel das Formular <b>frmPersonal <\/b>aus Bild 2. Die GUI-Schicht enth&auml;lt nur Methoden f&uuml;r den Zugriff auf die Business-Schicht, auf keinen Fall kann sie direkt auf eine der darunter liegenden Schichten zugreifen. Das ist bei herk&ouml;mmlichen Access-Anwendungen der Fall: Hier werden Datenherkunft und Steuerelemente direkt an die Datenschicht gebunden. Andersherum kann keine der anderen Schichten auf die GUI-Schicht zugreifen &#8211; das ist eine der Hauptpr&auml;missen bei der Entwicklung mehrschichtiger Anwendungen. Sie minimieren damit die Abh&auml;ngigkeit, indem Sie daf&uuml;r sorgen, dass diese lediglich einseitig ist.<\/p>\n<p>Beim hier verwendeten Schichtenmodell greift die GUI-Schicht ausschlie&szlig;lich auf die Objekte der Business-Schicht zu: auf die Datenobjekte mit den anzuzeigenden Informationen und auf das Controller-Objekt, das die Funktionen zum F&uuml;llen der Datenobjekte, zum Speichern der per GUI-Schicht ge&auml;nderten Inhalte der Datenobjekte, zum Speichern neuer Datenobjekte und zum L&ouml;schen bestehender Datenobjekte bereitstellt.<\/p>\n<h2>Die Business-Schicht<\/h2>\n<p>Die Business-Schicht enth&auml;lt zwei Typen von Objekten: Der erste Typ repr&auml;sentiert die in den Datens&auml;tzen der Tabellen enthaltenen Daten (Daten-Objekte), der zweite enth&auml;lt die Steuermechanismen f&uuml;r den Transfer der Daten zwischen der Datenzugriffsschicht und der GUI-Schicht (Controller-Objekte).<\/p>\n<p>Genau genommen ist das nicht ganz richtig: Die Controller-Objekte steuern zwar die Objekte der Datenzugriffsschicht und andere Objekte der Business-Schicht, aber die Kooperation zwischen der GUI-Schicht und den Controller-Objekten geht immer von der GUI-Schicht aus. Wie bereits erw&auml;hnt &#8211; die oberen Schichten k&ouml;nnen zwar auf die unteren zugreifen, aber niemals umgekehrt.<\/p>\n<p>Und da auch nie eine Schicht &uuml;bersprungen werden darf, muss die GUI-Schicht immer &uuml;ber die Business-Schicht auf die Datenzugriffsschicht zugreifen, die dann die gew&uuml;nschten Daten nach oben reicht.<\/p>\n<p>Wie viele und welche Objekte sich in der Business-Schicht befinden, h&auml;ngt von der Art der enthaltenen Daten und der Benutzeroberfl&auml;che ab. Sie werden vermutlich f&uuml;r jede Tabelle, die objektartige Daten enth&auml;lt, ein eigenes Objekt erstellen. Au&szlig;erdem m&uuml;ssen Sie entscheiden, ob Sie ein Controller-Objekt pro Element der Benutzeroberfl&auml;che oder vielleicht sogar ein gro&szlig;es Controller-Objekt verwenden. &Uuml;bersichtlicher d&uuml;rfte ein Objekt pro Formular sein.<\/p>\n<h2>Die Datenzugriffsschicht<\/h2>\n<p>Die Datenzugriffsschicht enth&auml;lt Datenzugriffsobjekte. Zu jedem Datenobjekt gibt es ein Datenzugriffsobjekt, das verschiedene Operationen ausf&uuml;hren kann:<\/p>\n<ul>\n<li class=\"aufz-hlung\">Erzeugen eines Datenobjekts auf Basis eines Datensatzes der zu Grunde liegenden Tabelle<\/li>\n<li class=\"aufz-hlung\">Erzeugen eines Recordsets mit Datens&auml;tzen als Suchergebnis mit vorgegebenen Kriterien<\/li>\n<li class=\"aufz-hlung\">Aktualisieren eines Datensatzes in der Datenbank auf Basis der in einem Datenobjekt enthaltenen Daten<\/li>\n<li class=\"aufz-hlung\">Anlegen eines neuen Datensatzes in der Datenbank<\/li>\n<li class=\"aufz-hlung\">L&ouml;schen eines Datensatzes aus der Datenbank<\/li>\n<\/ul>\n<p>Damit entkoppelt die Datenzugriffsschicht die Business-Schicht von den Daten. Der Vorteil liegt darin, dass Sie ohne Probleme die Datenquelle wechseln k&ouml;nnen &#8211; etwa, um von einem Access-Backend auf einen SQL-Server umzusteigen oder vielleicht sogar um eine XML-Datei als Datenquelle zu verwenden.<\/p>\n<p>Sie m&uuml;ssen lediglich die Klassen der Datenzugriffsschicht anpassen &#8211; die GUI-Schicht und die Business-Schicht bleiben von einem Wechsel der Datenquelle unber&uuml;hrt.<\/p>\n<p>Im Beispiel erfahren Sie, wie die f&uuml;nf Operationen eines Datenzugriffsobjekts aussehen. Man ben&ouml;tigt je ein Datenzugriffsobjekt pro Businessobjekt der Business-Schicht.<\/p>\n<p>Das Beispiel verwendet lediglich ein Datenzugriffsobjekt f&uuml;r den Zugriff auf die Datenbank per DAO. Sie k&ouml;nnen alternativ ein Datenzugriffsobjekt mit den gleichen Methoden, aber anderen Anweisungen f&uuml;r den Zugriff auf die Daten verwenden, um etwa die ADODB-Bibliothek statt der DAO-Bibliothek zu verwenden. Oder Sie erstellen ein drittes Datenzugriffsobjekt, das den Zugriff auf eine XML-Datei &uuml;ber das mit der Bibliothek MSXML gelieferte Document Object Model erm&ouml;glicht. Wenn Sie bez&uuml;glich des Datenzugriffs derart flexibel sein m&ouml;chten, empfiehlt sich die Verwendung einer Schnittstelle.<\/p>\n<h2>Die Datenschicht<\/h2>\n<p>Die Datenschicht enth&auml;lt die eigentlichen Daten. Im vorliegenden Beispiel ist das eine Tabelle in einer Access-Datenbank, es kann sich aber auch um eine Tabelle in einer SQL-Server-Datenbank oder um eine XML-Datei handeln.<\/p>\n<p>Diese Flexibilit&auml;t erhalten Sie durch die Aufteilung der Anwendung auf verschiedene Schichten &#8211; um etwa auf eine XML-Datei statt auf eine Access-Datenbank zuzugreifen, m&uuml;ssten Sie nur die Objekte der Datenzugriffsschicht anpassen. Die Benutzeroberfl&auml;che und die Business-Schicht bleiben unangetastet.<\/p>\n<h2>Beispielanwendung<\/h2>\n<p>Nach der Theorie zur&uuml;ck zum Beispiel: Das Formular aus Bild 2 soll nun also &uuml;ber die einzelnen Schichten auf die in der Datenbank gespeicherten Daten zugreifen, diese anzeigen, neue Datens&auml;tze anlegen oder bestehende Datens&auml;tze &auml;ndern oder l&ouml;schen. Beginnen wir also einfach mal am Anfang: den Zustand beim &Ouml;ffnen des Formulars.<\/p>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Person-Objekt erstellen<\/p>\n<p>W&auml;hrend der Arbeit mit dem Formular werden die Daten der Mitarbeiter, wie bereits erw&auml;hnt, nicht direkt aus der Tabelle ins Formular transportiert, sondern &uuml;ber ein spezielles Objekt, das im Prinzip mit einem Datensatz einer Tabelle vergleichbar ist, mit dem Unterschied, dass es statt Felder Eigenschaften aufweist und dass Sie seine Methoden selbst definieren k&ouml;nnen beziehungsweise m&uuml;ssen. Da Sie dieses Objekt auf Basis einer Klasse erzeugen und die Beschreibung dieser Klasse die folgenden Erl&auml;uterungen unn&ouml;tig behindern w&uuml;rde, legen Sie diese Klasse schnell vorab an. Die Klasse hei&szlig;t <b>clsPerson <\/b>und enth&auml;lt den Code aus Listing 1. Dazu geh&ouml;ren lediglich die Membervariablen, die die Eigenschaften speichern, sowie zu jeder Membervariablen eine <b>Property Get<\/b>&#8211; und eine <b>Property Let<\/b>-Methode. Wenn Sie sich noch nicht mit der Programmierung von Klassen auskennen, finden Sie in [1] weitere Informationen.<\/p>\n<p class=\"tabellenkopf\">Listing 1: Code der Klasse clsPerson<\/p>\n<pre>Option Compare Database\r\nOption Explicit\r\nDim mPersonalID As Long\r\nDim mVorname As String\r\nDim mNachname As String\r\nDim mPosition As String\r\nDim mAnrede As String\r\nDim mEinstellung As Date\r\nPublic Property Get PersonalID() As Long\r\n    PersonalID = mPersonalID\r\nEnd Property\r\nPublic Property Let PersonalID(lngPersonalID As Long)\r\n    mPersonalID = lngPersonalID\r\nEnd Property\r\nPublic Property Get Vorname() As String\r\n    Vorname = mVorname\r\nEnd Property\r\nPublic Property Let Vorname(strVorname As String)\r\n    mVorname = strVorname\r\nEnd Property\r\nPublic Property Get Nachname() As String\r\n    Nachname = mNachname\r\nEnd Property\r\nPublic Property Let Nachname(strNachname As String)\r\n    mNachname = strNachname\r\nEnd Property\r\nPublic Property Get Position() As String\r\n    Position = mPosition\r\nEnd Property\r\nPublic Property Let Position(strPosition As String)\r\n    mPosition = strPosition\r\nEnd Property\r\nPublic Property Get Anrede() As String\r\n    Anrede = mAnrede\r\nEnd Property\r\nPublic Property Let Anrede(strAnrede As String)\r\n    mAnrede = strAnrede\r\nEnd Property\r\nPublic Property Get Einstellung() As Date\r\n    Einstellung = mEinstellung\r\nEnd Property\r\nPublic Property Let Einstellung(strEinstellung As Date)\r\n    mEinstellung = strEinstellung\r\nEnd Property<\/pre>\n<h2>Initialisieren des Formulars<\/h2>\n<p>Direkt nach dem &Ouml;ffnen soll das Formular keinen Datensatz anzeigen. Lediglich das Kombinationsfeld <b>cboSchnellsuche <\/b>soll alle enthaltenen Personen zur Auswahl anbieten. Das F&uuml;llen dieses Steuerelements ist dann auch die erste Funktion, die programmiert und auf mehrere Schichten aufgeteilt werden soll.<\/p>\n<p>Der Beginn sieht unspektakul&auml;r aus: Die beim &Ouml;ffnen des Formulars ausgel&ouml;ste Routine initialisiert das im Kopf des Moduls deklarierte Controller-Objekt und ruft die Prozedur <b>cboSchnellsucheAktualisieren <\/b>auf. Das Controller-Objekt befindet sich im &Uuml;brigen in einer eigenen Klasse, die Sie weiter unten erstellen. Den Code aus Listing 2 speichern Sie im Klassenmodul des Formulars <b>frmPersonal<\/b>. Au&szlig;erdem m&uuml;ssen Sie noch die Eigenschaft <b>Beim &Ouml;ffnen <\/b>auf den Wert <b>[Ereignisprozedur] <\/b>einstellen.<\/p>\n<p class=\"tabellenkopf\">Listing 2: Initialisieren des Formulars<\/p>\n<pre>Dim objController As clsController\r\nPrivate Sub Form_Open(Cancel As Integer)\r\n    Set objController = New clsController\r\n    cboSchnellsucheAktualisieren\r\nEnd Sub<\/pre>\n<p>Die Prozedur <b>cboSchnellsucheAktualisieren<\/b> soll das Kombinationsfeld mit den in der Tabelle <b>tblPersonal<\/b> enthaltenen Daten f&uuml;llen. Unter Access brauchen Sie daf&uuml;r keine einzige Codezeile, sondern weisen einfach der Eigenschaft <b>Datensatzherkunft <\/b>eine passende Abfrage zu. Bei der Programmierung einer mehrschichtigen Anwendung wird das Ganze hingegen schon m&auml;chtig interessant &#8211; Sie f&uuml;llen dazu zun&auml;chst in der Datenzugriffsschicht ein <b>Collection<\/b>-Objekt, das dann nach oben gereicht und von der GUI-Schicht in das Kombinationsfeld gef&uuml;llt wird.<\/p>\n<p><!--30percent--><\/p>\n<p>Fangen Sie also ganz unten an und erstellen Sie eine Klasse namens <b>clsPersonal_Datenzugriffsobjekt<\/b>. Dieser f&uuml;gen Sie eine Methode namens <b>Find <\/b>hinzu, die nichts anderes erledigen soll, als eine <b>Collection <\/b>mit allen in der Tabelle <b>tblPersonal <\/b>enthaltenen Mitarbeitern zur&uuml;ckzuliefern. Um sp&auml;teren Anwendungszwecken vorzubeugen, f&uuml;gen Sie dieser direkt einen optionalen Parameter namens <b>varSearch <\/b>hinzu, dem Sie eine Zeichenkette mit einer <b>WHERE<\/b>-Klausel &uuml;bergeben k&ouml;nnen &#8211; dazu jedoch sp&auml;ter mehr. Die komplette Routine finden Sie in Listing 3. Sie durchl&auml;uft in einer <b>Do While<\/b>-Schleife alle in der Tabelle gespeicherten Datens&auml;tze und schreibt diese in jeweils ein eigenes Objekt des Typs <b>clsPerson<\/b>. Jedes dieser Objekte landet dann in der Collection <b>objPersonen<\/b>. Wichtig ist hierbei, dass beim Anf&uuml;gen der Objekte an die Collection der Prim&auml;rschl&uuml;sselwert des Datensatzes mit dem zweiten Parameter der <b>Add<\/b>-Methode der Collection als Schl&uuml;ssel des aktuellen Elements angeh&auml;ngt wird. Anderenfalls k&ouml;nnen Sie die Elemente der Collection sp&auml;ter nicht mehr identifizieren. Die Collection dient im &Uuml;brigen als R&uuml;ckgabewert der Funktion <b>Find <\/b>an die aufrufende Instanz.<\/p>\n<p class=\"tabellenkopf\">Listing 3: Die Find-Methode des Datenzugriffsobjekts clsPerson_Datenzugriffsobjekt<\/p>\n<pre>Public Function Find(Optional varSearch As Variant) As Collection\r\n    ''On Error GoTo Find_Err\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strSQL As String\r\n    Dim objPerson As clsPerson\r\n    Dim objPersonen As Collection\r\n    Set db = CurrentDb\r\n    strSQL = &quot;SELECT * FROM tblPersonal&quot;\r\n    If Not IsMissing(varSearch) Then\r\n        strSQL = strSQL &amp; &quot; WHERE &quot; &amp; varSearch\r\n    End If\r\n    Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)\r\n    Set objPersonen = New Collection\r\n    Do While Not rst.EOF\r\n        Set objPerson = New clsPerson\r\n        With objPerson\r\n            .PersonalID = rst!PersonalID\r\n            .Vorname = Nz(rst!Vorname, &quot;&quot;)\r\n            .Nachname = Nz(rst!Nachname, &quot;&quot;)\r\n            .Position = Nz(rst!Position, &quot;&quot;)\r\n            .Anrede = Nz(rst!Anrede, &quot;&quot;)\r\n            .Einstellung = Nz(rst!Einstellung, &quot;&quot;)\r\n        End With\r\n        objPersonen.Add objPerson, CStr(objPerson.PersonalID)\r\n        rst.MoveNext\r\n    Loop\r\n    Set Find = objPersonen\r\nFind_Exit:\r\n    On Error Resume Next\r\n    Set db = Nothing\r\n    Exit Function\r\nFind_Err:\r\n    GoTo Find_Exit\r\nEnd Function<\/pre>\n<p>Wer ruft nun diese Funktion auf und wohin geht die Collection Nun, daf&uuml;r ist das Controller-Objekt zust&auml;ndig. Dieses legen Sie ebenfalls zun&auml;chst in Form einer eigenen Klasse namens <b>clsController <\/b>an.<\/p>\n<p>F&uuml;r den Aufruf der <b>Find<\/b>-Methode ist in diesem Fall die kleine Methode <b>GetPersons<\/b> zust&auml;ndig. Diese Methode liefert ein <b>Collection<\/b>-Objekt zur&uuml;ck, das in dem Objekt <b>objPersonen <\/b>gespeichert wird. Die Methode sieht wie in Listing 4 aus.<\/p>\n<p class=\"tabellenkopf\">Listing 4: Die Methode GetPersons der Klasse clsController<\/p>\n<pre>Public Function GetPersons() As Collection\r\n    Set objPersonen = objPerson_Datenzugriffsobjekt.Find\r\n    Set GetPersons = objPersonen\r\nEnd Function<\/pre>\n<p>Nun kennt diese Methode das Objekt <b>objPerson_Datenzugriffsobjekt <\/b>aber noch gar nicht. Das wird ihm indirekt wiederum von der dar&uuml;berliegenden Schicht, n&auml;mlich der GUI-Schicht mitgeteilt. Indirekt deshalb, weil diese das Controller-Objekt instanziert und dadurch das im Controller-Objekt enthaltene Ereignis <b>Class_Initialize <\/b>ausgel&ouml;st wird. Damit dies alles geschieht, f&uuml;gen Sie auch noch die Deklarationen und die Prozedur <b>Class_Initialize <\/b>aus Listing 5 zur Klasse <b>clsController <\/b>hinzu.<\/p>\n<p class=\"tabellenkopf\">Listing 5: Initialisieren der Klasse clsController der Business-Schicht<\/p>\n<pre>Dim objPerson_Datenzugriffsobjekt As clsPerson_Datenzugriffsobjekt\r\nDim objPersonen As Collection\r\nPrivate Sub Class_Initialize()\r\n    Set objPerson_Datenzugriffsobjekt = New clsPerson_Datenzugriffsobjekt\r\nEnd Sub<\/pre>\n<p>Nun endlich k&ouml;nnen Sie auch der GUI-Schicht die f&uuml;r das F&uuml;llen des Kombinationsfelds notwendige Funktionalit&auml;t hinzuf&uuml;gen. Diese manifestiert sich in Form der Routine <b>cboSchnellsucheAktualisieren<\/b> und kann nicht nur beim &Ouml;ffnen des Formulars, sondern auch durch andere Ereignisse aufgerufen werden. Die Routine aus Listing 6 deklariert zun&auml;chst ein Objekt des Typs <b>clsPerson <\/b>und eine Collection. Die oben vorgestellte Methode <b>GetPersons <\/b>des Controller-Objekts liefert (&uuml;ber die <b>Find<\/b>-Methode des Datenzugriffsobjekts) die Collection mit den einzelnen Personen-Objekten. Diese durchl&auml;uft die Routine in einer <b>For Each<\/b>-Schleife und tr&auml;gt den Vor- und den Nachnamen in eine Semikola-separierte Zeichenkette ein. Diese muss die Routine nun nur noch der Datensatzherkunft des Kombinationsfelds zur Schnellauswahl zuweisen &#8211; fertig! Nat&uuml;rlich m&uuml;ssen Sie die Eigenschaft<b> Herkunftstyp <\/b>des Kombinationsfelds noch auf <b>Wertliste <\/b>sowie die beiden Eigenschaften <b>Spaltenanzahl <\/b>und <b>Spaltenbreite <\/b>auf <b>2 <\/b>und <b>0cm<\/b> einstellen.<\/p>\n<p class=\"tabellenkopf\">Listing 6: Zuweisen einer Datensatzgruppe mit allen Personen an das Kombinationsfeld zur Schnellsuche<\/p>\n<pre>Private Sub cboSchnellsucheAktualisieren()\r\n    Dim objPerson As clsPerson\r\n    Dim objPersonen As Collection\r\n    Dim str As String\r\n    Set objPersonen = objController.GetPersons\r\n    If Not objPersonen Is Nothing Then\r\n        For Each objPerson In objPersonen\r\n            str = str &amp; objPerson.PersonalID &amp; &quot;;&quot; &amp; objPerson.Nachname _\r\n                &amp; &quot;, &quot; &amp; objPerson.Vorname &amp; &quot;;&quot;\r\n        Next objPerson\r\n        Me.cboSchnellsuche.RowSource = str\r\n    Else\r\n        MsgBox &quot;Personenliste konnte nicht geladen werden.&quot;\r\n    End If\r\nEnd Sub<\/pre>\n<p>Wenn Sie nun das Formular in der Formularansicht &ouml;ffnen und das Kombinationsfeld aufklappen, sollte sich etwa ein Bild wie in Bild 3 pr&auml;sentieren. Gl&uuml;ckwunsch! Sie haben Ihr erstes Kombinationsfeld mittels objektorientierter, mehrschichtiger Datenbankentwicklung gef&uuml;llt. Zugegebenerma&szlig;en ist der Aufwand nicht gerade gering, aber die fehlende Datenbindung und die gew&uuml;nschte Flexibilit&auml;t erfordern diesen Weg. Nun geht es aber schnell weiter &#8211; immerhin soll der ausgew&auml;hlte Datensatz ja auch noch komplett im Formular angezeigt werden.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_02\/MehrschichtigeAnwendung-web-images\/pic003_opt.jpeg\" alt=\"pic003.tif\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 3: Kombinationsfeld mit Daten aus einer Collection von Personen-Objekten<\/span><\/b><\/p>\n<h2>Ausw&auml;hlen und anzeigen eines Datensatzes<\/h2>\n<p>Beim F&uuml;llen des Formulars kommt wiederum die Klasse <b>clsPerson <\/b>ins Spiel. Sie dient als Transportmittel der Daten aus der immer noch in der Klasse <b>objController <\/b>befindlichen Collection <b>objPersonen<\/b>.<\/p>\n<p>Ja, genau: Nach der Auswahl des anzuzeigenden Datensatzes aus dem Kombinationsfeld greift die Anwendung nicht etwa &uuml;ber die Zwischenschichten auf die Datenschicht zu, sondern bezieht die Informationen aus der im Controller zwischengespeicherten Collection, die alle im Kombinationsfeld ausw&auml;hlbaren Personen in Form von Objekten des Typs <b>clsPerson <\/b>enth&auml;lt. Die Ereigniseigenschaft <b>Nach Aktualisierung <\/b>l&ouml;st dabei &uuml;ber die Prozedur aus Listing 7 die Routine in Listing 8 aus.<\/p>\n<p class=\"tabellenkopf\">Listing 7: Diese Prozedur ruft die eigentliche Routine zum Laden eines Mitarbeiters auf.<\/p>\n<pre>Private Sub cboSchnellsuche_AfterUpdate()\r\n    MitarbeiterLaden\r\nEnd Sub<\/pre>\n<p class=\"tabellenkopf\">Listing 8: Diese Routine wird nach der Auswahl eines Eintrags des Kombinationsfelds aufgerufen.<\/p>\n<pre>Private Sub MitarbeiterLaden()\r\n    Dim objPerson As clsPerson\r\n    Set objPerson = objController.LoadPerson(Me!cboSchnellsuche)\r\n    objController.SavePerson Nz(Me!txtPersonalID, 0), Me!txtVorname, _\r\n         Me!txtNachname, Me!txtPosition, Me!txtAnrede, Me!txtEinstellung\r\n    With objPerson\r\n        Me!txtPersonalID = .PersonalID\r\n        Me!txtVorname = .Vorname\r\n        Me!txtNachname = .Nachname\r\n        Me!txtPosition = .Position\r\n        Me!txtAnrede = .Anrede\r\n        Me!txtEinstellung = .Einstellung\r\n    End With\r\nEnd Sub<\/pre>\n<p>Und das sieht so aus: Nach dem Deklarieren der Objektvariablen <b>objPerson <\/b>wird diese mit Hilfe der Methode <b>LoadPerson <\/b>des Controller-Objekts (siehe weiter unten) gef&uuml;llt. Als Parameter wird dabei das gebundene Feld des Kombinationsfeldes &uuml;bergeben, das die <b>PersonalID <\/b>des anzuzeigenden Mitarbeiters enth&auml;lt (s. Listing 9).<\/p>\n<p class=\"tabellenkopf\">Listing 9: Weiterdelegieren des Ladens von Personendaten in das entsprechende Objekt<\/p>\n<pre>Public Function LoadPerson(lngPersonalID As Long) As clsPerson\r\n    Dim objPerson As clsPerson\r\n    Set objPerson = objPersonen(CStr(lngPersonalID))\r\n    If objPerson Is Nothing Then\r\n        Set LoadPerson = objPerson_Datenzugriffsobjekt.Read(lngPersonalID)\r\n    Else\r\n        Set LoadPerson = objPerson\r\n    End If\r\nEnd Function<\/pre>\n<p>Zum F&uuml;llen der Eigenschaften mit den Inhalten der Felder des Datensatzes mit der gesuchten <b>PersonalID <\/b>ist die Methode <b>LoadPerson <\/b>des Controller-Objekts zust&auml;ndig.<\/p>\n<p>Diese Methode deklariert zun&auml;chst ein Objekt des Typs <b>clsPersonen <\/b>und weist diesem das Objekt aus der Collection <b>objPersonen <\/b>mit dem passenden Wert der Eigenschaft <b>PersonID <\/b>zu, der in der Collection als Schl&uuml;sselwert eines jeden Elements gespeichert ist.<\/p>\n<p>Sollte ein solches Objekt einmal nicht in der Collection zu finden sein, greift die Funktion &uuml;ber die Methode <b>Read <\/b>des Objekts <b>objPersonDAO <\/b>auf die in der Datenschicht beziehungsweise der Datenbank enthaltenen Daten zu &#8211; dazu sp&auml;ter mehr.<\/p>\n<p>Anderenfalls dient die in der Auflistung vorgefundene Instanz des gesuchten Personenobjekts der R&uuml;ckgabewert der Methode (s. Listing 9).<\/p>\n<p class=\"tabellenkopf\">Listing 17: Vorbereitung der Aktualisierung eines Datensatzes im Controller-Objekt<\/p>\n<pre>Private Function UpdatePerson(lngPersonalID As Long, strVorname As String, _\r\n        strNachname As String, strPosition As String, strAnrede As String, _\r\n        strEinstellung As String)\r\n    Dim objPerson As clsPerson\r\n    Set objPerson = New clsPerson\r\n    With objPerson\r\n        .PersonalID = lngPersonalID\r\n        .Vorname = strVorname\r\n        .Nachname = strNachname\r\n        .Position = strPosition\r\n        .Anrede = strAnrede\r\n        .Einstellung = strEinstellung\r\n    End With\r\n    objPerson_Datenzugriffsobjekt.Update objPerson\r\nEnd Function<\/pre>\n<h2>Einlesen von Personen, die nicht in der Collection enthalten sind<\/h2>\n<p>F&uuml;r den in diesem Beispiel eigentlich nicht vorgesehenen Fall, dass ein Mitarbeiter-Objekt angezeigt werden soll, das nicht in der Collection <b>objPersonen <\/b>enthalten ist, greift die Methode <b>LoadPerson <\/b>des Controller-Objekts mit der <b>Read<\/b>-Methode der Datenzugriffsklasse auf den in der Datenbank gespeicherten Personendatensatz zu.<\/p>\n<p>Diese &ouml;ffnet zun&auml;chst eine Datensatzgruppe aller Datens&auml;tze mit der &uuml;bergebenen <b>PersonalID<\/b>, wobei die Anzahl logischerweise <b>1 <\/b>ist.<\/p>\n<p>Die dort enthaltenen Informationen werden nun in ein frisch instanziertes <b>Personen<\/b>-Objekt eingetragen, das anschlie&szlig;end als R&uuml;ckgabewert der Funktion festgelegt wird (s. Listing 10).<\/p>\n<p class=\"tabellenkopf\">Listing 10: Einlesen eines Datensatzes in ein Objekt der Business-Schicht<\/p>\n<pre>Public Function Read(PersonID As Long) As clsPerson\r\n    On Error GoTo Read_Err\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim objPerson As clsPerson\r\n    Set db = CurrentDb\r\n    Set rst = db.OpenRecordset(&quot;SELECT * FROM tblPersonen &quot; _\r\n        &amp; &quot;WHERE [PersonID] = &quot; &amp; PersonID, dbOpenDynaset)\r\n    Set objPerson = New clsPerson\r\n    With objPerson\r\n        .PersonalID = rst![PersonalID]\r\n        .Vorname = rst![Vorname]\r\n        .Nachname = rst![Nachname]\r\n        .Position = rst![Position]\r\n        .Anrede = rst![Anrede]\r\n        .Einstellung = rst![Einstellung]\r\n    End With\r\n    Set Read = objPerson\r\nRead_Exit:\r\n    On Error Resume Next\r\n    Set objPerson = Nothing\r\n    rst.Close\r\n    Set rst = Nothing\r\n    Set db = Nothing\r\n    Exit Function\r\nRead_Err:\r\n    GoTo Read_Exit\r\nEnd Function<\/pre>\n<p>Von hier aus geht es dann &uuml;ber die Business-Schicht direkt in die GUI-Schicht. Dort wartet die Routine aus Listing 9 bereits und tr&auml;gt die Eigenschaften der gew&uuml;nschten Person in die entsprechenden Textfelder des Formulars ein.<\/p>\n<h2>Neuen Datensatz anlegen<\/h2>\n<p>Das Anlegen neuer Datens&auml;tze verl&auml;uft in ungebundenen Formularen erstaunlich ruhig. Damit ist nat&uuml;rlich vor allem die Interaktion mit der Datenbank gemeint, denn bevor man nicht die <b>OK<\/b>-Schaltfl&auml;che anklickt oder zu einem anderen beziehungsweise neuen Datensatz wechselt, geschieht gar nichts.<\/p>\n<p>Falls noch ein Datensatz im Formular angezeigt wird, soll dieser allerdings erst einmal gespeichert und das Formular geleert werden.<\/p>\n<p>Dazu verwenden Sie die Schaltfl&auml;che mit der Beschriftung <b>Neu<\/b>. Die durch das Klicken ausgel&ouml;ste Routine (s. Listing 11) ruft eine weitere Funktion namens <b>FormularLeeren <\/b>auf, die alle Textfelder des Formulars leert (s. Listing 12).<\/p>\n<p class=\"tabellenkopf\">Listing 11: Diese Routine ruft eine weitere Routine zum Leeren des Formulars auf.<\/p>\n<pre>Private Sub cmdNeu_Click()\r\n    FormularLeeren\r\nEnd Sub<\/pre>\n<p class=\"tabellenkopf\">Listing 12: Leeren der Textfelder des Formulars<\/p>\n<pre>Private Sub FormularLeeren()\r\n    With Me\r\n        !txtPersonalID = Null\r\n        !txtVorname = &quot;&quot;\r\n        !txtNachname = &quot;&quot;\r\n        !txtPosition = &quot;&quot;\r\n        !txtAnrede = &quot;&quot;\r\n        !txtEinstellung = &quot;&quot;\r\n    End With\r\nEnd Sub<\/pre>\n<h2>Speichern eines Datensatzes<\/h2>\n<p>Nach der Eingabe der Daten ist es sinnvoll, diese zu speichern. Das soll entweder beim Anzeigen eines anderen Datensatzes, beim Anlegen eines weiteren Datensatzes oder beim Schlie&szlig;en des Formulars geschehen. Letzteres erfolgt mit der Schaltfl&auml;che <b>cmdOK<\/b>. Diese l&ouml;st die Prozedur aus Listing 13 aus, die &#8211; Sie ahnen es bereits &#8211; eine weitere Methode des Controllers aufruft, die wiederum eine Methode der Klasse <b>clsPerson_Datenzugriffsobjekt <\/b>einsetzt.<\/p>\n<p class=\"tabellenkopf\">Listing 13: Ausl&ouml;sen des Speicher-Vorgangs im Formular<\/p>\n<pre>Private Sub cmdOK_Click()\r\n    Me!txtPersonalID = objController.SavePerson( _\r\n        Nz(Me!txtPersonalID, 0), Me!txtVorname, Me!txtNachname, _\r\n        Me!txtPosition, Me!txtAnrede, Me!txtEinstellung)\r\n    cboSchnellsucheAktualisieren\r\n    DoCmd.Close acForm, Me.Name\r\nEnd Sub<\/pre>\n<p>Die Routine pr&uuml;ft, ob der Datensatz bereits einen Wert im Feld <b>PersonalID <\/b>hat oder nicht. Falls nicht, handelt es sich um einen neuen Datensatz und die Funktion <b>CreatePerson <\/b>des Controller-Objekts wird aufgerufen.<\/p>\n<p>Anderenfalls ist der Datensatz vorhanden und es wird nur die neue Version gespeichert. Daf&uuml;r ist die Funktion <b>UpdatePerson <\/b>zust&auml;ndig.<\/p>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Datensatz neu anlegen oder aktualisieren<\/p>\n<p>Der beim Bet&auml;tigen der <b>OK<\/b>-Schaltfl&auml;che im Formular befindliche Datensatz kann bereits in der Datenbank vorhanden sein oder auch nicht. Ein eindeutiges Kennzeichen daf&uuml;r ist das Vorhandensein eines Wertes in der Eigenschaft <b>PersonID<\/b>.<\/p>\n<p>Diese ID wird nur beim Anlegen eines Objekts in der Datenbank erledigt.<\/p>\n<p>Die Methode <b>SavePerson <\/b>(s. Listing 14) pr&uuml;ft dies und reicht die zu speichernden Daten entweder an die Routine <b>CreatePerson <\/b>oder <b>UpdatePerson <\/b>weiter.<\/p>\n<p class=\"tabellenkopf\">Listing 14: Diese Methode entscheidet, ob ein Objekt in der Datenbank gespeichert oder nur aktualisiert werden soll.<\/p>\n<pre>Public Function SavePerson(lngPersonalID As Long, strVorname As String, _\r\n        strNachname As String, strPosition As String, strAnrede As String, _\r\n        strEinstellung As String) As Long\r\n    If lngPersonalID = 0 Then\r\n        lngPersonalID = CreatePerson(strVorname, strNachname, _\r\n            strPosition, strAnrede, strEinstellung)\r\n    Else\r\n        UpdatePerson lngPersonalID, strVorname, strNachname, _\r\n            strPosition, _strAnrede, strEinstellung\r\n    End If\r\n    SavePerson = lngPersonalID\r\nEnd Function<\/pre>\n<h2>Neuen Datensatz anlegen<\/h2>\n<p>Die Funktion <b>CreatePerson <\/b>gibt die Daten des anzulegenden Objekts an die Methode <b>Create <\/b>des Datenzugriffsobjekts weiter.<\/p>\n<p>Dies geschieht in der Form, dass zun&auml;chst ein Personen-Objekt mit den Eigenschaften der Person erstellt und dieses dann an das Datenzugriffsobjekt &uuml;bergeben wird (s. Listing 15).<\/p>\n<p class=\"tabellenkopf\">Listing 15: Die Funktion CreatePerson des Controller-Objekts erwartet die zu speichernden Eigenschaften des Person-Objekts als Parameter.<\/p>\n<pre>Public Function CreatePerson(strVorname As String, strNachname As _\r\n        String, strPosition As String, strAnrede As String, strEinstellung _\r\n        As String) As Long\r\n    Dim objPerson As clsPerson\r\n    Set objPerson = New clsPerson\r\n    With objPerson\r\n        .Vorname = strVorname\r\n        .Nachname = strNachname\r\n        .Position = strPosition\r\n        .Anrede = strAnrede\r\n        .Einstellung = strEinstellung\r\n    End With\r\n    If objPerson_Datenzugriffsobjekt.Create(objPerson) = True Then\r\n        CreatePerson = objPerson.PersonalID\r\n    End If\r\n    Set objPerson = Nothing\r\nEnd Function<\/pre>\n<p>Die <b>Create<\/b>-Methode k&uuml;mmert sich nun um das Anlegen des Datensatzes in der Tabelle <b>tblPersonen<\/b>. Dabei wird neben dem Anlegen des Datensatzes auch die Eigenschaft <b>PersonID <\/b>mit dem in der Tabelle angelegten Wert gef&uuml;llt.<\/p>\n<p>Wenn das Anlegen erfolgreich war, liefert die Methode den Wert <b>True <\/b>zur&uuml;ck. Die Methode aus Listing 16 kann dann aus dem per Referenz &uuml;bergebenen Objekt den neuen Wert der Eigenschaft <b>PersonID <\/b>auslesen.<\/p>\n<p class=\"tabellenkopf\">Listing 16: Die Create-Methode des Datenzugriffsobjekts legt einen neuen Datensatz auf Basis des &uuml;bergebenen Objekts an.<\/p>\n<pre>Public Function Create(objPerson As clsPerson) As Long\r\n    On Error GoTo Create_Err\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Set db = CurrentDb\r\n    Set rst = db.OpenRecordset _\r\n    (&quot;SELECT * FROM tblPersonal WHERE [PersonalID] = 0&quot;, dbOpenDynaset)\r\n    With objPerson\r\n        rst.AddNew\r\n        rst![Vorname] = .Vorname\r\n        rst![Nachname] = .Nachname\r\n        rst![Position] = .Position\r\n        rst![Anrede] = .Anrede\r\n        rst![Einstellung] = .Einstellung\r\n        .PersonalID = rst![PersonalID]\r\n        rst.Update\r\n    End With\r\n    Create = True\r\nCreate_Exit:\r\n    On Error Resume Next\r\n    Set rst = Nothing\r\n    Set db = Nothing\r\nExit Function\r\n    Create_Err:\r\n    Create = False\r\n    GoTo Create_Exit\r\nEnd Function<\/pre>\n<h2>Aktualisieren eines Datensatzes<\/h2>\n<p>Das Aktualisieren bestehender Datens&auml;tze erfolgt analog.<\/p>\n<p>Diesmal ruft die Methode <b>SavePerson<\/b> die Funktion <b>UpdatePerson <\/b>auf, wobei im Vergleich zum Anlegen des Datensatzes der Wert der Eigenschaft <b>PersonID <\/b>mit &uuml;bergeben wird (s. Listing 17). Die Methode <b>Update<\/b> des Datenzugriffsobjekts &ouml;ffnet eine Datensatzgruppe, die lediglich einen Datensatz enth&auml;lt &#8211; den mit der &uuml;bergebenen <b>PersonalID<\/b> (s. Listing 18).<\/p>\n<p class=\"tabellenkopf\">Listing 18: Aktualisieren eines Datensatzes auf Basis des passenden Objekts<\/p>\n<pre>Public Function Update(objPerson As clsPerson)\r\n    On Error GoTo Update_Err\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Set db = CurrentDb\r\n    Set rst = db.OpenRecordset(&quot;SELECT * FROM tblPersonal WHERE [PersonalID] = &quot; _\r\n        &amp; objPerson.PersonalID, dbOpenDynaset)\r\n    With objPerson\r\n        rst.Edit\r\n        rst![Vorname] = .Vorname\r\n        rst![Nachname] = .Nachname\r\n        rst![Position] = .Position\r\n        rst![Anrede] = .Anrede\r\n        rst![Einstellung] = .Einstellung\r\n        rst.Update\r\n    End With\r\n    Update = True\r\nUpdate_Exit:\r\n    On Error Resume Next\r\n    Set rst = Nothing\r\n    Set db = Nothing\r\n    Exit Function\r\nUpdate_Err:\r\n    Update = False\r\n    GoTo Update_Exit\r\nEnd Function<\/pre>\n<h2>L&ouml;schen eines Datensatzes<\/h2>\n<p>Um den aktuell im Formular angezeigten Datensatz zu l&ouml;schen, klicken Sie auf die <b>L&ouml;schen<\/b>-Schaltfl&auml;che. Diese ruft wie gehabt die Business-Schicht auf und &uuml;bergibt den Wert des Feldes <b>PersonalID <\/b>an die dortige Methode <b>DeletePerson<\/b>. Nach erfolgreichem L&ouml;schvorgang leert die Routine das Formular, aktualisiert das Kombinationsfeld zur Schnellsuche und leert auch dieses (s. Listing 19).<\/p>\n<p class=\"tabellenkopf\">Listing 19: Starten des L&ouml;schvorgangs<\/p>\n<pre>Private Sub cmdLoeschen_Click()\r\n    If objController.DeletePerson(Me!txtPersonalID) = True Then\r\n        FormularLeeren\r\n        cboSchnellsucheAktualisieren\r\n        Me!cboSchnellsuche = Null\r\n    End If\r\nEnd Sub<\/pre>\n<p>Die Methode<b> DeletePerson <\/b>des Controller-Objekts (s. Listing 20) reicht die ID des zu l&ouml;schenden Datensatzes direkt an die Methode <b>Delete <\/b>der Datenzugriffsklasse weiter.<\/p>\n<p class=\"tabellenkopf\">Listing 20: Die Methode DeletePerson des Controller-Objekts<\/p>\n<pre>Public Function DeletePerson(lngPersonalID As Long) As Boolean\r\n    DeletePerson = objPerson_Datenzugriffsobjekt.Delete(lngPersonalID)\r\nEnd Function<\/pre>\n<p>Diese l&ouml;scht den Datensatz mit einem <b>DELETE<\/b>-Statement und gibt bei Gelingen den Wert <b>True <\/b>zur&uuml;ck (s. Listing 21).<\/p>\n<p class=\"tabellenkopf\">Listing 21: Entfernen eines Datensatzes aus der Tabelle tblPersonen<\/p>\n<pre>Public Function Delete(PersonalID As Long)\r\n    On Error GoTo Delete_Err\r\n    Dim db As DAO.Database\r\n    Set db = CurrentDb\r\n    db.Execute &quot;DELETE FROM tblPersonal WHERE [PersonalID] = &quot; &amp; PersonalID, dbFailOnError\r\n    Delete = True\r\nDelete_Exit:\r\n    On Error Resume Next\r\n    Set db = Nothing\r\n    Exit Function\r\nDelete_Err:\r\n    Delete = False\r\n    GoTo Delete_Exit\r\nEnd Function<\/pre>\n<h2>Bei Datensatzwechsel speichern<\/h2>\n<p>&Uuml;blicherweise geht man heutzutage davon aus, dass &Atilde;&#8220;nderungen an einem Datensatz beim Wechsel zu einem anderen Datensatz gespeichert werden. Dies soll auch hier der Fall sein: Dazu f&uuml;gen Sie an zwei Stellen der GUI-Schicht noch eine Anweisung zum Speichern des aktuellen Datensatzes ein.<\/p>\n<p>Die passende Anweisung lautet folgenderma&szlig;en:<\/p>\n<pre>objController.SavePerson Nz(Me!txtPersonalID, 0), Me!txtVorname, Me!txtNachname, Me!txtPosition, Me!txtAnrede, Me!txtEinstellung<\/pre>\n<p>Einf&uuml;gen m&uuml;ssen Sie diese in die Routine, die nach dem Ausw&auml;hlen eines neuen Mitarbeiters &uuml;ber das Schnellsuche-Kombinationsfeld ausgel&ouml;st wird, und zwar vor dem F&uuml;llen der Textfelder mit den neuen Werten, sowie in die Routine, die das Formular zum Anlegen eines neuen Mitarbeiters leert. In der Beispieldatenbank finden Sie die notwendige Zeile bereits in den entsprechenden Routinen.<\/p>\n<p>Fraglich ist nur, wie man einmal vorgenommene &Atilde;&#8220;nderungen wieder verwerfen kann. Dazu braucht man noch eine <b>Abbrechen<\/b>-Schaltfl&auml;che. Diese soll einfach den Zustand des bearbeiteten Mitarbeiters vor der Bearbeitung wiederherstellen.<\/p>\n<p>Das ist allerdings nicht ganz so einfach, denn immerhin gibt es bei ungebundenen Textfeldern, die ja hier nun einmal vorliegen, keine Eigenschaft wie <b>OldValue<\/b>, mit der man die urspr&uuml;nglichen Werte ermitteln k&ouml;nnte, oder gar eine <b>Undo<\/b>-Funktion.<\/p>\n<p>Also l&auml;dt man in dieser Situation einfach wieder den letzten gespeicherten Stand in das Formular. Die <b>Abbrechen<\/b>-Schaltfl&auml;che statten Sie dazu mit der Routine aus Listing 22 aus.<\/p>\n<p class=\"tabellenkopf\">Listing 22: Die Abbrechen-Schaltfl&auml;che macht &Atilde;&#8220;nderungen am aktuellen Mitarbeiter r&uuml;ckg&auml;ngig.<\/p>\n<pre>Private Sub cmdAbbrechen_Click()\r\n    If IsNull(Me!txtPersonalID) Then\r\n        FormularLeeren\r\n    Else\r\n        MitarbeiterLaden\r\n    End If\r\nEnd Sub<\/pre>\n<h2>Businesslogik und mehr<\/h2>\n<p>Dieses Beispiel zeigt, wie Sie zwei Pattern der objektorientierten Welt mit VBA und Access einsetzen &#8211; das Model View Controller-Pattern und das DAO-Pattern (wobei DAO auch Data Access Objects bedeutet, aber nichts mit der DAO-Klasse von Access zu tun hat) &#8211; diese Stichw&ouml;rter nur f&uuml;r diejenigen, die sich ein wenig genauer mit der Materie auseinandersetzen m&ouml;chten.<\/p>\n<p>Was bringt das Ganze nun Immerhin geht hier eine Menge Code f&uuml;r eine Aufgabe drauf, die sonst mit wenigen Zeilen zu l&ouml;sen w&auml;re. Daf&uuml;r erhalten Sie aber auch eine Menge mehr Flexibilit&auml;t und &Uuml;bersicht.<\/p>\n<p>Sie k&ouml;nnen die Businessregeln komplett in der Business-Schicht versenken, was sich nat&uuml;rlich erst dann auszahlt, wenn Sie nicht nur mit einem, sondern mit mehreren Objekten, einer umfangreicheren Benutzeroberfl&auml;che und einem dementsprechenden Datenmodell arbeiten.<\/p>\n<p>Die Validierung der Daten kann man je nach Anforderung in die GUI-Schicht verfrachten oder in die Business-Schicht integrieren. Wenn Werte direkt nach der Eingabe in ein Textfeld auf ihre G&uuml;ltigkeit gepr&uuml;ft werden sollen, macht eine entsprechende Validierung in der GUI-Schicht sicher Sinn. Gesch&auml;ftsregeln, die sich auf einen gr&ouml;&szlig;eren Zusammenhang beziehen und gegebenenfalls mehrere Projekte betreffen, lassen sich bestens in der Business-Schicht unterbringen.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Der Artikel zeigt, dass mehrschichtige Architekturen eine code-intensive Sache sind. Wenn Sie das Prinzip des hier verwendeten Model-View-Controller-Modells einmal verinnerlicht haben, ist die Programmierung aber reine Flei&szlig;arbeit. <\/p>\n<p>Es bleiben jedoch noch einige Funktionen offen: Wie werden verkn&uuml;pfte Daten angezeigt, wie erfolgt die Validierung und welche Vorteile bietet die objektorientierte Entwicklung noch Dieses Thema wird sicher in einer der folgenden Ausgaben von Access im Unternehmen weiter behandelt.<\/p>\n<p class=\"zwischen-berschriftquellen\">Quellen<\/p>\n<p class=\"quellen\">[1] Ausgabe 5\/2004, Objektorientiertes Programmieren mit Klassen, Shortlink 232<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>MehrschichtigeAnwendung.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/{1F9C9436-B9E5-4EB9-9371-9A268DA3992B}\/aiu_453.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Access ist &#8211; rein architektonisch betrachtet &#8211; auf das Anlegen monolithischer Anwendungen mit Objekten mit starker Abh&auml;ngigkeit ausgelegt. Formulare und Berichte sind starr mit den zu Grunde liegenden Datenherk&uuml;nften verbunden und der Code liegt jeweils im passenden Formular- oder Berichtsmodul oder, falls n&ouml;tig, in einem Standard- oder Klassenmodul. Wie man auch mit Access mehrschichtige Anwendungen baut, zeigt dieser Beitrag.<\/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":[66022007,662007,44000023,44000025],"tags":[],"class_list":["post-55000453","post","type-post","status-publish","format-standard","hentry","category-66022007","category-662007","category-Mit_Formularen_arbeiten","category-VBA_und_Programmiertechniken"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Mehrschichtige Anwendungen - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Mehrschichtige Anwendungen\" \/>\n<meta property=\"og:description\" content=\"Access ist - rein architektonisch betrachtet - auf das Anlegen monolithischer Anwendungen mit Objekten mit starker Abh&auml;ngigkeit ausgelegt. Formulare und Berichte sind starr mit den zu Grunde liegenden Datenherk&uuml;nften verbunden und der Code liegt jeweils im passenden Formular- oder Berichtsmodul oder, falls n&ouml;tig, in einem Standard- oder Klassenmodul. Wie man auch mit Access mehrschichtige Anwendungen baut, zeigt dieser Beitrag.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-15T16:27:29+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg02.met.vgwort.de\/na\/5c35e9e8ff794e26ab4ad06ea049af58\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"27\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Mehrschichtige Anwendungen\",\"datePublished\":\"2020-05-15T16:27:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/\"},\"wordCount\":4223,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5c35e9e8ff794e26ab4ad06ea049af58\",\"articleSection\":[\"2\\\/2007\",\"2007\",\"Mit Formularen arbeiten\",\"VBA und Programmiertechniken\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/\",\"name\":\"Mehrschichtige Anwendungen - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5c35e9e8ff794e26ab4ad06ea049af58\",\"datePublished\":\"2020-05-15T16:27:29+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5c35e9e8ff794e26ab4ad06ea049af58\",\"contentUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5c35e9e8ff794e26ab4ad06ea049af58\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Mehrschichtige_Anwendungen\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Mehrschichtige Anwendungen\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Mehrschichtige Anwendungen - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/","og_locale":"de_DE","og_type":"article","og_title":"Mehrschichtige Anwendungen","og_description":"Access ist - rein architektonisch betrachtet - auf das Anlegen monolithischer Anwendungen mit Objekten mit starker Abh&auml;ngigkeit ausgelegt. Formulare und Berichte sind starr mit den zu Grunde liegenden Datenherk&uuml;nften verbunden und der Code liegt jeweils im passenden Formular- oder Berichtsmodul oder, falls n&ouml;tig, in einem Standard- oder Klassenmodul. Wie man auch mit Access mehrschichtige Anwendungen baut, zeigt dieser Beitrag.","og_url":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-15T16:27:29+00:00","og_image":[{"url":"http:\/\/vg02.met.vgwort.de\/na\/5c35e9e8ff794e26ab4ad06ea049af58","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"27\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Mehrschichtige Anwendungen","datePublished":"2020-05-15T16:27:29+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/"},"wordCount":4223,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/5c35e9e8ff794e26ab4ad06ea049af58","articleSection":["2\/2007","2007","Mit Formularen arbeiten","VBA und Programmiertechniken"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/","url":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/","name":"Mehrschichtige Anwendungen - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/5c35e9e8ff794e26ab4ad06ea049af58","datePublished":"2020-05-15T16:27:29+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#primaryimage","url":"http:\/\/vg02.met.vgwort.de\/na\/5c35e9e8ff794e26ab4ad06ea049af58","contentUrl":"http:\/\/vg02.met.vgwort.de\/na\/5c35e9e8ff794e26ab4ad06ea049af58"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Mehrschichtige_Anwendungen\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Mehrschichtige Anwendungen"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55000453"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000453\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55000453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55000453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55000453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}