Wer Software für einen Kunden entwickelt und nicht ständig an der Produktiv-Version arbeitet, steht irgendwann vor der Anforderung, die Produktiv-Version gegen die neue Version auszutauschen. Dieser Beitrag zeigt eine Möglichkeit, ein solches Update ohne Reibungsverluste durchzuführen.
Zum Update einer Datenbank können mehrere Faktoren gehören. Es kann sein, dass man nur einen kleinen Fehler im Code behoben hat und dem Benutzer die neue Version zukommen lassen möchte – kein Problem, man tauscht einfach das am besten in einer Frontend-Backend-Konfiguration enthaltene Frontend aus und verknüpft das neue Frontend mit den Tabellen des alten Backends.
Oder man stellt fest, dass man ein wichtiges Feld im Datenmodell vergessen hat. Das wird schon schwieriger: Das Backend mit dem Datenmodell kann man nicht so schnell austauschen, denn es enthält ja die aktuell benutzten Daten. In dem Fall wird man am einfachsten zum Kunden fahren und das Datenmodell vor Ort ändern oder dies per Fernwartung erledigen. Ist beides nicht möglich, leitet man den Kunden durch diesen Vorgang, was erfahrungsgemäß je nach dessen Fertigkeiten kompliziert werden kann.
Sobald jedoch mehr als ein Kunde mit derselben Datenbank arbeitet oder häufige oder viele änderungen vorgenommen werden sollen, fällt die oben beschriebene Vorgehensweise flach.
Vielleicht treffen ja auch änderungen am Frontend und am Backend zusammen, was zumindest dann der Regelfall ist, wenn das Backend betroffen ist – änderungen am Frontend sind dann nicht ganz unwahrscheinlich.
Diese Kombination trifft sich gut, denn wenn man dem Kunden ein neues Frontend mit geänderter Benutzeroberfläche oder Anwendungslogik verpasst, kann man änderungen am Datenmodell gleich durch diese neue Version des Frontends erledigen lassen.
Genau wie einige andere Vorgänge, die normalerweise beim Öffnen der Datenbank ablaufen – also etwa das Prüfen des Backends und der Verknüpfungen zu den Tabellen im Backend oder die Konsistenzprüfung bestehender Daten -, können Sie hier auch änderungen am Datenmodell des Backends durchführen lassen.
Von A nach B
Wenn Sie eine Datenbankanwendung nur für einen Kunden erstellt haben und weiterentwickeln, liegt eine komfortable Situation vor. Ausgehend davon, dass der Kunde neue Versionen immer direkt einsetzt, brauchen Sie beim Start des neuen Frontends nur ein paar Codezeilen aufzurufen, welche die seit dem letzten Update angefallenen änderungen am Datenmodell durchführen.
Jedes weitere Update geht dann vom jeweils durch die Vorversion hergestellten Zustand aus und passt das Backend seinerseits an.
Ganz anders sieht es aus, wenn zwei oder mehr Benutzer die Dienste Ihrer Datenbank in Anspruch nehmen, wobei „mehrere Benutzer“ nicht im Sinne von Mehrbenutzerumgebung, sondern von eigenständigen Anwendern mit eigenem Front- und Backend gemeint ist.
Nehmen wir an, es gäbe der Einfachheit halber nur zwei Benutzer, von denen der eine die Anwendung in Version 1.0 betreibt und der andere bereits ein Update auf die Version 1.1 vorgenommen hat – der erste Benutzer hat dies schlicht ausgelassen.
Wenn Sie nun eine neue Version 1.2 veröffentlichen, können Sie nicht mehr einfach nur davon ausgehen, dass diese Version überall die gleichen Voraussetzungen antrifft, um die in Version 1.2 enthaltenen änderungen etwa am Backend durchzuführen.
Eine Tabelle, die in Version 1.1 hinzugefügt wurde und in Version 1.2 um ein weiteres Feld ergänzt werden soll, wäre beim Update von Version 1.0 auf Version 1.2 noch gar nicht vorhanden und würde so zu einem Fehler beim Hinzufügen des Feldes zur nicht vorhandenen Tabelle führen.
Bevor jemand auf die Idee kommt: Es kommt natürlich nicht infrage, dass der Kunde nacheinander alle Zwischenversionen installiert, wenn er den Schritt von Version 1.0 etwa zu Version 1.6 machen möchte.
Sie müssen also zumindest eine Überprüfung einbauen, mit welcher Version die Anwender arbeiten, und selbst dafür sorgen, dass die änderungen der einzelnen Versionen inkrementell durchgeführt werden – auch wenn der Anwender gleich mehrere Versionen überspringt.
Verknüpft und zugenäht
Als kleines Bonbon kommt noch hinzu, dass neu im Backend angelegte Tabellen erst noch verknüpft und die Verknüpfungen geänderter Tabellen aktualisiert werden müssen.
Bevor Sie eine neue Version der Software verteilen, werden Sie vermutlich schon alle notwendigen Verknüpfungen angelegt haben, sodass diese beim Update nicht mehr hinzugefügt werden müssen (es wäre auch Unsinn, ein Frontend auszutauschen und dieses beim ersten Start nach dem Austausch dazu zu bringen, sich selbst anzupassen, indem es einige Verknüpfungen hinzufügt).
Normalerweise sollten Sie aufgeteilten Datenbanken aber ohnehin einen Mechanismus mitgeben, der jeweils beim Anwendungsstart prüft, ob das Backend an Ort und Stelle ist, und die Verknüpfungen aktualisiert.
Und da dieser doch recht eng mit dem Anlegen neuer Tabellen beim Update der Anwendung verknüpft ist, liefern wir diesen Mechanismus in diesem Beitrag gleich mit.
Aufbau
Die nachfolgend vorgestellten Techniken setzen ein paar Faktoren voraus. Zunächst einmal besteht das besprochene System wie erwähnt aus einer Frontend- und einer Backenddatenbank, wobei die Benutzeroberfläche (also Formulare, Berichte und Menüleisten), die Anwendungslogik und die Abfragen im Frontend und die durch die Anwendung zu verwaltenden Daten in entsprechenden Tabellen im Backend gespeichert sind.
Damit Sie im Frontend auf die in den Tabellen des Backends enthaltenen Daten genau so zugreifen können, als ob diese im Frontend enthalten wären, legen Sie im Frontend eine Reihe von Verknüpfungen an, die das Datenbankfenster (Access 2003) oder der Navigationsbereich (Access 2007) mit entsprechenden Symbolen ausgestattet im Bereich Tabellen aufführt (siehe Bild 1).
Bild 1: Verknüpfte Tabellen werden durch besondere Symbole markiert.
In dieser Abbildung sehen Sie gleich eine recht wichtige Tabelle, die entscheidend für die hier vorgestellte Technik ist: tbl_VersionBE. Diese Tabelle enthält nur ein Feld namens Version, das die Nummer der aktuellen Version speichert. In Bild 2 ist dies die Nummer 0.904. Wichtig: Natürlich muss sich diese Tabelle im Backend befinden und ist mit dem Frontend lediglich verknüpft! Anderenfalls würde diese ja mit jeder neuen Version des Frontends ausgetauscht.
Bild 2: Tabelle zum Speichern der Versionsnummer des Backends
Dem Frontend können Sie eine ähnliche Tabelle beispielsweise mit dem Namen tbl_VersionFE hinzufügen. Einen neuen Wert enthält die Tabelle mit jeder neuen Version des Frontends.
Es kann sehr nützlich sein, wenn Sie Informationen über die Version von Frontend und Backend an gut sichtbarer Stelle unterbringen, damit der Benutzer Ihnen diese bei Problemen gegebenenfalls leicht mitteilen kann; für die Fehlerbehandlung sind diese Informationen natürlich ebenfalls hochinteressant (vielleicht lassen Sie sich die Fehlermeldungen bei Laufzeitfehlern per E-Mail zusenden, dann sollten Informationen über die Version von Frontend und Backend nicht fehlen – neben Modul, Prozedurname, Zeile, Fehlernummer et cetera).
Ablauf
Bevor wir uns den Code ansehen, der für das Verknüpfen und Updaten des Backends sorgt (nicht vergessen: das Update des Frontends erfolgt ganz einfach durch seinen Austausch, das neue Frontend führt dann das Update des Backends aus!), schauen wir uns den Ablauf im Überblick an.
Diesen verschafft Abb. 3, die alle Schritte der im Modul mdlGlobal befindlichen Funktion StartDB der Beispieldatenbank darstellt, wobei die einzelnen Schritte durchnummeriert sind, damit wir uns im Text besser darauf beziehen können (auch in den folgenden Listings taucht diese Nummerierung auf).
Bild 3: Ablauf beim Verknüpfen und Updaten des Backends durch ein neues Frontend
Im ersten Schritt (1) liest die Routine aus Listing 1 über die Hilfsfunktion GetCurrentBackendPath (s. Listing 2) den Pfad und den Dateinamen des Backends aus, auf den sich die im Frontend enthaltenen Tabellenverknüpfungen beziehen.
Listing 1: Die Routine StartDB steuert das Update des Backends und die Prüfung und Aktualisierung der Tabellenverknüpfungen.
Const cStrVersionTable = "tbl_VersionBE" Public Function StartDB() Dim dbFrontend As DAO.Database, dbBackend As DAO.Database Dim strBackendpath As String Dim bolBackendFound As Boolean, bolTablesExist As Boolean, bolStartSuccessful As Boolean Dim strOldVersion As String, strNewVersion As String, strCurrentVersion As String Set dbFrontend = CurrentDb GetCurrentBackendPath dbFrontend, cStrVersionTable, strBackendpath On Error Resume Next bolBackendFound = Len(Dir(strBackendpath)) > 0 On Error GoTo 0 If bolBackendFound = False Then strBackendpath = dbFrontend.OpenRecordset("SELECT Optionvalue FROM tblOptions_Backend " _ & "WHERE Optionname = 'Backendpath'").Fields(0) On Error Resume Next bolBackendFound = Len(Dir(strBackendpath)) > 0 On Error GoTo 0 End If Do While bolTablesExist = False Do While bolBackendFound = False strBackendpath = OpenFileName(Left(strBackendpath, InStrRev(strBackendpath, "\")), _ "Backend auswählen", "Access 2007-Datenbank (*.accdb)|Access 2003 und älter-Datenbank " _ & "(*.mdb)|Alle Dateien (*.*)") If Len(strBackendpath) = 0 Then Exit Function bolBackendFound = Len(Dir(strBackendpath)) > 0 Loop Set dbBackend = OpenDatabase(strBackendpath) If CheckTables(dbFrontend, dbBackend, True) = True Then strOldVersion = dbBackend.OpenRecordset("SELECT Version FROM tbl_VersionBE").Fields(0) strNewVersion = dbFrontend.OpenRecordset("SELECT NeueVersion FROM tblVersionen " _ & "ORDER BY ReihenfolgeID DESC").Fields(0) Do While Not strCurrentVersion = strNewVersion If UpdateVersion(dbFrontend, dbBackend, strOldVersion, strCurrentVersion) = True Then strOldVersion = strCurrentVersion Else MsgBox "Das Update wurde nicht durchgeführt." Exit Function End If Loop bolTablesExist = CheckTables(dbFrontend, dbBackend, False) = True Else bolBackendFound = False End If If bolTablesExist = False Then If MsgBox("In der Datenbank '" & strBackendpath & "' fehlen eine oder mehrere der benötigten " _ & "Tabellen." & vbCrLf & vbCrLf & "Klicken Sie auf 'OK', um eine andere Datenbank " _ & "auszuwählen oder auf 'Abbrechen', um die Anwendung zu beenden.", _ vbOKCancel + vbExclamation, "Falsche Datenbank") = vbCancel Then DoCmd.Quit Exit Function End If End If Loop If ReconnectBackend(dbFrontend, strBackendpath) = True Then dbFrontend.Execute "UPDATE tblOptions_Backend SET Optionvalue = '" & strBackendpath _ & "' WHERE Optionname = 'Backendpath'", dbFailOnError bolStartSuccessful = True End If End Function
Listing 2: Die Funktion GetCurrentBackendPath ermittelt die Datenbank, mit der die Tabellenverknüpfungen aktuell verknüpft sind.
Public Function GetCurrentBackendPath(dbFrontend As _ DAO.Database, strTable As String, _ strBackendpath As String) As Boolean Dim strTemp As String strTemp = dbFrontend.TableDefs(strTable).connect strTemp = Mid(strTemp, InStr(1, strTemp, "DATABASE=") + 9) If InStr(1, strTemp, ";") > 0 Then strTemp = Left(strTemp, InStr(1, strTemp, ";") - 1) End If If Not Len(strTemp) = 0 Then strBackendpath = strTemp GetCurrentBackendPath = True End If End Function
Dazu ist noch kein Kontakt mit dem Backend erforderlich; die Information wird per DAO über die Connect-Eigenschaft einer der Tabellenverknüpfungen ermittelt (siehe weiter hinten unter Aktuelle Backend-Datenbank ermitteln).
Hier gibt es zwei Möglichkeiten, die in (2) geprüft werden. Dabei testet die Routine mit der Dir-Funktion, ob die für die Verknüpfung angegebene Datei überhaupt existiert, und findet eines der folgenden beiden Ergebnisse vor:
- Das Frontend wurde soeben als Update über das bestehende Frontend kopiert. Dann zeigen die Tabellenverknüpfungen noch auf die Backenddatenbank, die der Entwickler verwendet hat – es geht bei (3) weiter.
- Das Update ist bereits erfolgt. Dann sind die Tabellenverknüpfungen bereits auf das Backend beim Benutzer eingestellt und eine erneute Auswahl des Backends entfällt. Möglicherweise stimmt der Pfad des Backends auf dem Entwicklersystem auch gleich mit dem Pfad beim Benutzer überein oder der Entwickler passt den Pfad vor der Weitergabe entsprechend an – in diesem Fall geht bei (4) weiter.
Wenn die in den Tabellenverknüpfungen angegebene Datenbankdatei nicht vorhanden ist, schaut die Routine in eine spezielle Tabelle namens tblOptions_Backend, die den Pfad des zuletzt verwendeten Backends speichert, aber auch durch den Entwickler auf einen Pfad voreingestellt werden kann (3). Dies bietet die Möglichkeit, ein Frontend, das auf mehreren Rechnern des Kunden installiert werden und auf das gleiche, auf einem Server liegende Backend zugreifen soll, direkt auf dieses einzustellen.
Im nächsten Schritt (5) prüft die Routine den Wert der Boolean-Variablen bolTablesExist, die erst den Wert True erhält, wenn alle Tabellen vorhanden und verknüpft sind – und dies ist beim Einstieg in die Routine noch nicht der Fall. Also geht es nun weiter zu (6), wo erneut eine Prüfung erfolgt, ob die zuvor ermittelte Datei überhaupt existiert. Ist das nicht der Fall, stimmt also auch der in der Tabelle tblOptions_Backend gespeicherte Wert nicht oder dieser ist leer. Dann muss der Benutzer in den sauren Apfel beißen und das Backend mit einem Datei öffnen-Dialog selbst auswählen (7). Dieses Spiel wiederholt sich so lange, bis der Benutzer eine vorhandene Datenbankdatei ausgewählt hat.
Ist dies geschehen, heißt dies noch lange nicht, dass er auch die richtige Datenbank gewählt hat – es kann ja auch irgendeine Access-Datei sein. Also prüft die Routine im nächsten Schritt (8), ob die gefundene Datenbank eine Tabelle namens tbl_VersionBE enthält, und liest deren Wert gegebenenfalls ein. Ist diese Tabelle nicht vorhanden, kann der Benutzer sich entscheiden, ob er ein anderes Backend auswählen (Schritte 9-10-11-5-6-7) oder die Anwendung beenden möchte (Schritte 9-10-11-12).
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo