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