Ein neues Feature in VFP 7.0 sind die Events des Datenbankcontainers. Sie stellen eine völlig neue Möglichkeit dar, datenbezogene Aktionen zu kontrollieren. Die Events werden bei jeder Aktion gefeuert, die den Datenbankcontainer betrifft. Diese Session zeigt zunächst die Funktionsweise der Events. In der praktischen Anwendung werden Möglichkeiten gezeigt, Datenbankaktionen zu kontrollieren, bzw. zu steuern. Abgerundet wird diese Session durch einen Ausblick auf sich daraus ergebende Anwendungsmöglichkeiten.
DBC-Events sind neu in Visual FoxPro 7.0. Als dieses Dokument entstand, gab es noch nicht mal eine Beta-Version des Produktes. Alles, was mir vorlag war ein Pre-Release, das sich beim Aufruf noch mit dem Arbeitstitel "Sedona" meldet und eher leidlich läuft. Es kann sich also noch einiges ändern. Methodenaufrufe und Parameter sind noch nicht komplett fertig. Vielleicht haben wir in der endgültigen VFP 7.0 dann dafür neue Bugs. Sogar die grundsätzliche Funktionsweise einiger Aufrufe könnte eventuell noch geändert werden. Besonders auffällig waren bisher die Änderungen bezüglich der Reaktion auf Returncodes aus den Events. Beachten Sie dies bitte, wenn Sie das Dokument lesen, bzw. anwenden. Es werden im Dokument einige Vorgehensweisen gezeigt, wie man die DBC-Events testen kann. Probieren Sie im Zweifelsfall lieber noch mal anhand der Testbeispiele aus, was in Ihrer VFP-Version wirklich passiert, welcher Bug zwischenzeitlich bereinigt wurde, bzw. welcher dazugekommen ist. Unter den Samples von VFP ist auch ein Beispiel für das Verhalten und die Anwendung von DBC-Events, zu finden unter "Neuerungen in Visual FoxPro 7.0".
DBC-Events sind Ereignisse, die feuern, wenn auf den Datenbankcontainer selbst oder auf eines seiner Objekte zugegriffen wird. So wird zum Beispiel ein Event gefeuert, wenn eine Tabelle geöffnet wird. Das setzt allerdings voraus, dass die Tabelle zu einem Datenbankcontainer gehört. Dabei ist es unerheblich, ob das Event programmatisch, automatisch oder über die Entwicklungsumgebung ausgelöst wird. Es feuert, wenn die Tabelle in einem Programm geöffnet wird, wenn ein View geöffnet wird, der diese Tabelle benutzt und wenn man die Tabelle über das Datensitzungs-Fenster (SET) öffnet.
Ob die DBC-Events eingeschaltet oder ausgeschaltet sind, wird dadurch bestimmt, ob die DBC-Eigenschaft DBCEVENTS auf .T. oder .F. steht. Standardmäßig sind die Events für den Datenbankcontainer ausgeschaltet. Man muss sie erst über DBSETPROP oder über den neuen Eigenschafts-Dialog des DBC (siehe unten) einschalten. Der durch die Events erzeugte Overhead ist relativ gering, er liegt nach einigen Schätzungen etwa bei 2-3%. Die Events werden z.B. durch folgenden Aufruf eingeschaltet, wobei die Datenbank geöffnet sein muss:
DBSETPROP(, "DATABASE", "DBCEVENTS", .T.)
Wenn man die DBC-Events eingeschaltet hat, ist der Datenbankcontainer nicht mehr mit einer früheren VFP-Version zu öffnen. Man erhält bei dem Versuch, diesen DBC mit VFP 6.0 zu öffnen, die Fehlermeldung, dass der DBC mit einer späteren Version von FoxPro erstellt wurde. Sobald man allerdings die Events wieder ausschaltet, ist der Datenbankcontainer wieder rückwärtskompatibel.
Der Code für DBC-Events kann entweder in Stored Procedures des Datenbankcontainers oder in einem separaten Programm abgelegt werden. Wo der Code liegt, wird in einer Eigenschaft des Datenbankcontainers festgelegt. Dementsprechend kann man nicht Stored Procedures und Programm mischen oder die Events in mehreren Programmdateien ablegen. Der Aufruf von weiteren Programmen aus den Events heraus ist aber natürlich möglich.
Soll der Event-Code in einem separaten Programm abgelegt werden, dann muss das Programm bereits existieren, wenn man dies im DBC einstellt. Außerdem muss sich das Programm im selben Verzeichnis wie der Datenbankcontainer befinden, was die Anwendungsmöglichkeiten solch ausgelagerter Programme doch einschränkt. Ansonsten erhält man beim Einstellen der Eigenschaft, bzw. beim Öffnen des DBC die Fehlermeldung, dass die entsprechende Datei nicht gefunden werden konnte. Es ist zu vermuten, dass dieser Bug in der endgültigen Version von VFP 7 bereinigt sein wird. Bevor man im DBC einstellt, wo der Event-Code gespeichert ist, muss die Verwendung der Events aktiviert worden sein (Eigenschaft DBCEVENTS)! Der Programmname wird in der Eigenschaft DBCEVENTFILENAME abgelegt. entweder über den Eigenschafts-Dialog des DBC (s.u.) oder über DBSETPROP wie folgt eingestellt:
DBSETPROP(, "DATABASE", "DBCEVENTFILENAME", )
Die Namen der Events selbst beginnen alle mit dem Prefix DBC_, dann folgt ein sprechender Begriff als Bezeichnung für das Event (z.B. DBC_BeforeOpenTable für das Event, das feuert, bevor eine Tabelle geöffnet wird).
Eine weitere Neuerung in VFP 7 ist der neue Eigenschafts-Dialog des Datenbankcontainers:
Neu ist der linke Teil des Dialogs, der sich ausschließlich mit den DBC-Events beschäftigt. Oben befindet sich eine Liste mit sämtlichen Events. Die Events, für die bereits Code geschrieben wurde, sind durch fette Schrift gekennzeichnet. Die Namen der Events werden hier ohne den Prefix DBC_ angezeigt. Unter der Liste ist eine kurze Erklärung für das gerade ausgewählte Event zu sehen.
Unten links befinden sich die Checkboxen "Set Events On" und "Events File" (lassen wir uns mal überraschen, wie das in der deutschen Version übersetzt wird). Mit "Set Events On" wird die Eigenschaft DBCEVENTS eingestellt. Mit "Events File" wird eingestellt, ob der Event-Code in einer separaten Datei verwaltet werden soll. Darunter steht der Dateiname des Eventcode-Programms und ein Button zum Starten des Dateiauswahl-Dialogs, um die Datei zu suchen.
Wenn man ein Event, für das noch kein Code besteht, auswählt und dann auf den Button "Edit Code" klickt, wird automatisch ein Template für den Eventcode erstellt. So braucht man nicht selbst die Parameter aus der grandiosen Hilfedatei herauszusuchen, sondern kann einfach das Template editieren.
Zum Tracen der Events kann man einfach sämtliche Events auswählen, die Templates erzeugen lassen und WAIT WINDOWS in den Event-Code einbauen. So kann man zu jeder Zeit feststellen, welches Event wann feuert. Entsprechend vorbereitete Event-Codes befinden sich auch bei den Beispielen auf der Konferenz-CD.
Es gibt Events für nahezu alle Aktionen, die man mit, bzw. im Datenbankcontainer ausführen kann. Es gibt Events für den Datenbankcontainer selbst, für Tabellen, Views, Beziehungen und Connections. Die Namen der Events selbst beginnen alle mit dem Prefix DBC_, dann folgt ein sprechender Begriff als Bezeichnung für das Event (z.B. DBC_BeforeOpenTable für das Event, das feuert, bevor eine Tabelle geöffnet wird).
Für die meisten Aktionen gibt es ein Before- und ein After- Event. Wenn man im Before-Event als Returncode .F. zurückgibt, wird die entsprechende Aktion nicht ausgeführt. So wird z.B. das Öffnen des Tabellen-Designers unterbunden, wenn das Event DBC_BeforeModifyTable() den Returncode .F. zurückgibt.
Dabei ist zu beachten, dass die Before-Events zwar vor der eigentlichen Aktion feuern, aber nach der Parameterprüfung. Wenn man also versucht, eine Tabelle umzubenennen, die es gar nicht gibt, feuert NICHT das Event DBC_BeforeRenameTable()!
After-Events feuern, nachdem die Aktion ausgeführt wurde. Also z.B. nachdem eine Tabelle umbenannt wurde (DBC_AfterRenameTable) oder nachdem der Tabellen-Dialog geschlossen wurde (DBC_AfterModifyTable). After-Events feuern nicht, wenn die Ausführung durch Rückgabe von .F. in einem Before-Event unterbunden wurde! Unglücklicherweise kann man nicht feststellen, ob die Ausführung des Events erfolgreich war, bzw. was geändert wurde. Zum Erkennen der Änderungen kann man sich nur im Before-Event den Status Quo des zu modifizierenden Objektes sichern und hinterher vergleichen.
Die Events verlangen unterschiedliche Parameter, weshalb sich das Erzeugen von Templates besonders empfiehlt. Es werden sich wahrscheinlich auch noch einige Parameter in der endgültigen Version von VFP 7 geändert haben.
Eine weitere Neuerung ist die VFP-Funktion ALANGUAGE(). Sie schreibt die Namen der verfügbaren Kommandos, Funktionen oder Events und ggf. Anzahl der Parameter in ein Array. Man übergibt den Namen eines Arrays und den Typ der Information, die man haben möchte. So kann man sich sämtliche möglichen DBC-Events in ein Array schreiben lassen und dieses auswerten.
Die Funktion wird wie folgt aufgerufen:
Folgende Werte sind für den Parameter nTyp möglich:
nTyp |
Typ |
Rückgabe |
---|---|---|
1 |
Kommandos |
Eindimensionales Array mit den Namen der in VFP 7 |
2 |
Funktionen |
Zweidimensionales Array den in VFP 7 verfügbaren
Funktionen. |
3 |
Basisklassen |
Eindimensionales Array mit den Namen der Basisklassen |
4 |
DBC-Events |
Zweidimensionales Array mit den in VFP 7 verfügbaren DBC-Events. (Beschreibung des Arrays siehe nTyp=2) |
DBC-Event | Parameter / Auslöser / Bemerkungen |
---|---|
DBC_OpenData | cDatabaseName, lExclusive, lNoupdate, lValidate Wenn eine Datenbank geöffnet wird oder wenn der Befehl MODIFY DATABASE für eine geschlossene Datenbank aufgerufen wird. Rückgabe von .F. verhindert die Ausführung, dann erscheint die Fehlermeldung, dass der Dateizugriff verweigert wurde. Achtung: Wenn der Event-Code in einer Stored Procedure abgelegt ist und man hier immer .F. zurückgibt, kann man den DBC nicht mehr über OPEN DATA / MODIFY DATA öffnen, um die Stored Procedure zu ändern. |
DBC_ModifyData | cDatabaseName, lNoWait, lNoEdit Wenn der Befehl MODIFY DATABASE für eine Datenbank aufgerufen wird. Rückgabe von .F. verhindert das Öffnen des Datenbankdialogs. |
DBC_Activate | cDatabaseName Wenn eine geöffnete Datenbank aktiviert wird. |
DBC_Deactivate | cDatabaseName Wenn eine Datenbank deaktiviert wird (z.B. durch Aktivierung einer anderen Datenbank oder beim schließen der Datenbank). |
DBC_CloseData | cDatabaseName, lAll Wenn eine Datenbank geschlossen wird. Außerdem wird das Event DBC_Deactivate gefeuert. |
DBC_BeforeValidateData | lRecover, lNoConsole, lPrint, lFile, cFileName Bevor eine Datenbank validiert wird. Rückgabe von .F. verhindert die Ausführung des Befehls. VALIDATE DATABASE kann jetzt auch programmatisch aufgerufen werden. |
DBC_AfterValidateData | lRecover, lNoConsole, lPrint, lFile, cFileName Nachdem der Befehl VALIDATE DATABASE beendet wurde. Man erhält allerdings keine Information darüber, ob der DBC gültig ist. |
DBC_PackData | - Wenn der Befehl PACK DATABASE aufgerufen wird. |
DBC_BeforeDBGetProp | cName, cType, cProperty Bevor eine Datenbankeigenschaft ermittelt wird. Im Gegensatz zu den anderen Before-Events wird die Ausführung der Funktion nicht verhindert, wenn dieses Event .F. zurückgibt. |
DBC_AfterDBGetProp | cName, cType, cProperty Nachdem eine Datenbankeigenschaft ermittelt wurde. |
DBC_BeforeDBSetProp | cName, cType, cProperty, ePropertyValue Bevor eine Datenbankeigenschaft geändert wird. Rückgabe von .F. verhindert die Ausführung. Dieses Event wird auch ausgelöst, bevor die Eigenschaft DBCEvents auf .F. gesetzt wird. |
DBC_AfterDBSetProp | cName, cType, cProperty, ePropertyValue Nachdem die Datenbankeigenschaft geändert wurde. Dieses Event wird auch ausgelöst, nachdem die Eigenschaft DBCEvents auf .T. gesetzt wurde. |
DBC-Events für Stored Procedures
DBC-Event | Parameter / Auslöser / Bemerkungen |
---|---|
DBC_BeforeModifyProc | - Bevor eine Stored Procedure eines geöffneten DBC verändert wird. Rückgabe von .F. verhindert die Änderung. |
DBC_AfterModifyProc | - Nachdem eine Stored Procedure gespeichert wurde. Das Schließen des Editors für Stored Procedures löst nicht dieses Event aus! Man kann nicht feststellen, was an der Prozedur geändert wurde. |
DBC_BeforeAppendProc | cFileName, nCodePage, lOverwrite Bevor die Stored Procedures eingefügt werden. Rückgabe von .F. verhindert das Einfügen der Stored Procedures. |
DBC_AfterAppendProc | cFileName, nCodePage, lOverwrite Nachdem die Stored Procedures eingefügt wurden |
DBC_BeforeCopyProc | cFileName, nCodePage, lAdditive Bevor die Stored Procedures kopiert werden. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterCopyProc | cFileName, nCodePage, lAdditive Nachdem die Stored Procedures kopiert wurden. |
DBC-Event | Parameter / Auslöser / Bemerkungen |
---|---|
DBC_BeforeAddTable | cTableName, cLongTableName Bevor eine freie Tabelle dem DBC hinzugefügt wird. Rückgabe von .F. verhindert die Ausführung |
DBC_AfterAddTable | cTableName, cLongTableName Nachdem eine freie Tabelle zum DBC hinzugefügt wurde. |
DBC_BeforeCreateTable | cTableName, cLongTableName Bevor eine Tabelle in einem geöffneten DBC erstellt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterCreateTable | cTableName, cLongTableName Nachdem eine Tabelle im DBC erstellt wurde. |
DBC_BeforeDropTable | cTableName, lRecycle Bevor eine Tabelle aus dem DBC entfernt und von der Festplatte gelöscht wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterDropTable | cTableName, lRecycle Nachdem eine Tabelle aus dem DBC entfernt und von der Festplatte gelöscht wurde. |
DBC_BeforeOpenTable | cTableName Bevor eine Tabelle oder ein View geöffnet wird. Rückgabe von .F. verhindert die Ausführung, dann erscheint die Fehlermeldung, dass der Dateizugriff verweigert wurde. Wenn die Tabelle oder der View unter einem anderen Alias geöffnet wird, dann wird der Alias anstatt des Tabellen-, bzw. Viewnamens an dieses Event übergeben. Dieses Event feuert auch für jede Tabelle, die Basis für den View ist, der geöffnet werden soll, unabhängig davon, ob die Tabelle bereits geöffnet ist oder nicht - und zwar sowohl beim Öffnen als auch beim REQUERY() des Views. Achtung: beim REQUERY() von Views feuern nur die Open-Events der Tabellen, nicht die des Views selbst! |
DBC_AfterOpenTable | cTableName Nachdem eine Tabelle oder ein View geöffnet wurde. Wenn eine Tabelle oder der View unter einem anderen Alias geöffnet wurde, dann wird der Alias anstatt des Tabellen-, bzw. Viewnamens an dieses Event übergeben. Dieses Event feuert auch für jede Tabelle, die Basis für den View ist, der geöffnet werden soll, unabhängig davon, ob die Tabelle bereits geöffnet war oder nicht, sowohl beim Öffnen als auch beim REQUERY() des Views. Achtung: beim REQUERY() von Views feuern nur die Open-Events der Tabellen, nicht die des Views selbst! |
DBC_BeforeCloseTable | cTableName Bevor eine Tabelle oder ein View geschlossen wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterCloseTable | cTableName Nachdem eine Tabelle oder ein View geschlossen wurde. |
DBC_BeforeRemoveTable | cTableName, lDelete, lRecycle Bevor eine Tabelle aus dem DBC entfernt wird. Rückgabe von .F. verhindert die Ausführung. Das Event feuert nicht bei DROP TABLE. |
DBC_AfterRemoveTable | cTableName, lDelete, lRecycle Nachdem eine Tabelle aus dem DBC entfernt wurde. Rückgabe von .F. verhindert die Ausführung. Das Event feuert nicht bei DROP TABLE. |
DBC_BeforeRenameTable | cPreviousName, cNewName Bevor eine Tabelle umbenannt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterRenameTable | cPreviousName, cNewName Nachdem eine Tabelle umbenannt wurde. Innerhalb dieser Prozedur gilt noch der alte Tabellenname! |
DBC_BeforeModifyTable | cTableName Bevor eine Tabelle, verändert wird. z.B. bevor der Tabellendialog aufgerufen wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterModifyTable | cTableName, lCancelled Nachdem eine Tabelle verändert wurde. z.B. nachdem der Tabellen-Designer beendet wurde. |
DBC-Event | Parameter / Auslöser / Bemerkungen |
---|---|
DBC_BeforeCreateOffline | cViewName, cPath Bevor ein View offline gesetzt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterCreateOffline | cViewName, cPath Nachdem ein View offline gesetzt wurde. |
DBC_BeforeDropOffline | cViewName Bevor ein Offline-View auf online gesetzt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterDropOffline | cViewName Nachdem ein Offline-View auf online gesetzt wurde. |
DBC_BeforeCreateView | cViewName,lRemote Bevor ein View erstellt wird, bzw. bevor der View-Designer geöffnet wird. Rückgabe von .F. verhindert die Ausführung. Beim Erstellen eines Views werden die Events DBC_BeforeOpenTable() und DBC_AfterOpenTable() der Tabellen, die die Datenquelle für den View darstellen, doppelt aufgerufen. |
DBC_AfterCreateView | cViewName,lRemote Nachdem ein View erstellt wurde, bzw. der neue View im View-Designer gespeichert wurde. Das Schließen des View-Designers selbst löst nicht dieses Event aus. |
DBC_BeforeDropView | cViewName Bevor ein View aus dem Datenbankcontainer entfernt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterDropView | cViewName Nachdem ein View aus dem Datenbankcontainer entfernt wurde. |
DBC_BeforeRenameView | cPreviousName, cNewName Bevor ein View umbenannt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterRenameView | cPreviousName, cNewName Nachdem ein View umbenannt wurde. |
DBC_BeforeModifyView | cViewName Vor dem Öffnen des View-Designers zum Ändern eines Views. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterModifyView | cViewName Nachdem der View im View-Designer gespeichert wurde. Das Event feuert auch, wenn der View im View-Designer gar nicht verändert wurde. Das Schließen des View-Designers selbst löst nicht dieses Event aus. |
DBC-Event | Parameter / Auslöser / Bemerkungen |
---|---|
dbc_BeforeAddRelation | cDBCName, cTableName, cRelationID, cRelatedChild, cRelatedTable, cRelatedTag Bevor eine persistente Beziehung erstellt wird. Rückgabe von .F. verhindert die Ausführung. |
dbc_AfterAddRelation | cDBCName, cTableName, cRelationID, cRelatedChild, cRelatedTable ,cRelatedTag Nachdem eine persistente Beziehung erstellt wurde. |
dbc_BeforeDropRelation | cDBCName, cRelationID, cTableName, cRelatedChild, cRelatedTable, cRelatedTag Bevor eine persistente Beziehung aus dem DBC entfernt wird. Rückgabe von .F. verhindert die Ausführung. |
dbc_AfterDropRelation | cDBCName, cRelationID, cTableName, cRelatedChild, cRelatedTable, cRelatedTag Nachdem eine persistene Beziehung aus dem DBC entfernt wurde. |
DBC-Event | Parameter / Auslöser / Bemerkungen |
---|---|
DBC_BeforeCreateConnection | cConnectionName, cDataSourceName,cUserID, cPassword, cConnectionString Bevor eine Connection erstellt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterCreateConnection | cConnectionName, cDataSourceName,cUserID, cPassword, cConnectionString Nachdem eine Connection erstellt wurde. |
DBC_BeforeRenameConnection | cPreviousName, cNewName Bevor eine Connection umbenannt wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterRenameConnection | cPreviousName, cNewName Nachdem eine Connection umbenannt wurde. DBC_BeforeModifyConnection cConnectionName Bevor der Connection-Designer zum Ändern einer Connection aufgerufen wird. Rückgabe von .F. verhindert die Ausführung. DBC_AfterModifyConnection cConnectionName Nachdem eine Connection im Connection-Designer geändert und gespeichert wurde. Das Schließen des Connection-Designers selbst löst dieses Event nicht aus. |
DBC_BeforeDeleteConnection | cConnectionName Bevor eine Connection gelöscht wird. Rückgabe von .F. verhindert die Ausführung. |
DBC_AfterDeleteConnection | cConnectionName Nachdem eine Connection gelöscht wurde. |
Die folgenden Beispiele zeigen den Ablauf von Events auf, die durch eine Aktion ausgelöst werden. Es gibt natürlich noch andere Aktionen, die gleich mehrere Events auslösen, aber hier soll nur exemplarisch gezeigt werden, wie die Events voneinander abhängen.
Die folgende Tabelle zeigt aufeinanderfolgende Aktionen und die dadurch ausgelösten Events in der entsprechenden Reihenfolge. Beim Öffnen einer Datenbank wird zunächst das Event DBC_OpenData() und dann das DBC_Activate() gefeuert. Wenn man dann eine andere Datenbank öffnet, wird die bisher aktive Datenbank deaktiviert und das entsprechende Event gefeuert. Beim Schließen der Datenbanken wird zunächst das Event DBC_CloseData des aktiven DBC gefeuert. Dann werden alle offenen Tabellen dieses DBCs geschlossen, mit Auslösung der entsprechenden Events. Erst danach wird der DBC deaktiviert. Danach wird der daraufhin aktive DBC mit entsprechender Eventauslösung geschlossen.
Aktion | Events |
---|---|
OPEN DATABASE data1 |
DBC_OPENDATA(data1 DBC_ACTIVATE(data1) |
OPEN DATABASE data2 |
DBC_OPENDATA(data2) DBC_DEACTIVATE(data1) DBC_ACTIVATE(data2) |
USE data2!table1 |
DBC_BeforeOpenTable(table1) DBC_AfterOpenTable(table1) |
CLOSE DATABASES ALL |
DBC_CloseData(data2) DBC_BeforeCloseTable(table1) DBC_AfterCloseTable(table1) DBC_Deactivate(data2) DBC_CloseData(data1) DBC_Deactivate(data1) |
Beim Erstellen eines Views werden die Events zum Öffnen der dazugehörigen Tabellen doppelt gefeuert. Ob dies ein Bug ist, der noch beseitigt wird, ist derzeit unklar.
Aktion | Events |
---|---|
CREATE VIEW view1 AS SELECT * FROM table1 |
DBC_BeforeCreateView (view1) DBC_BeforeOpenTable(table1) DBC_AfterOpenTable(table1) DBC_BeforeOpenTable(table1) DBC_AfterOpenTable(table1) DBC_AfterCreateView (view1) |
Beim Öffnen des lokalen Views werden die Open-Events der dazugehörigen Tabellen ebenfalls gefeuert.
Aktion | Events |
---|---|
USE view1 |
DBC_BeforeOpenTable(view1) DBC_BeforeOpenTable(table1) DBC_AfterOpenTable(table1) DBC_AfterOpenTable(view1) |
Im folgenden Beispiel wird direkt auf die VFP-Datenbank zugegriffen. In der Datenbank befindet sich ein Remote-View, der auf Tabellen einer anderen VFP-Datenbank zugreift (hier nur zu Demozwecken). Da Database-Events nicht über ODBC feuern, werden beim Öffnen des Remote Views nur die Events des DBC gefeuert, auf den direkt zugegriffen wird.
Aktion | Events |
---|---|
USE view2 |
DBC_BeforeOpenTable(view2) DBC_AfterOpenTable(view2) |
Einschränkungen in der Nutzbarkeit von DBC-Events gibt es natürlich leider auch. Dazu gehört die Tatsache, dass die Parameter von der aufgerufenen Funktion geprüft werden, bevor das entsprechende Event feuert. Außerdem können die übergebenen Parameter nicht verändert werden. Somit ist es nicht möglich, in den DBSetProp()-Mechanismus steuernd einzugreifen, wie man es bei Eigenschaften von Objekten über Access- und Assign-Methoden kann.
Ein erheblicher Wehrmustropfen ist, dass die DBC-Events nicht feuern, wenn man über ODBC auf den DBC zugreift. Das liegt daran, dass die ODBC-Treiber nicht weiterentwickelt wurden und auch nicht weiterentwickelt werden. Wenn man über ADO auf den DBC zugreift, feuern die Events.
Über Open-Events kann ein Logging für die Datenbank, oder auch ganz gezielt für bestimmte Tabellen durchgeführt werden. Man kann z.B. die Benutzer der Datenbank mit Zeitstempel in eine Protokolldatei aufnehmen (es sei denn, der Betriebsrat hat etwas dagegen....). Auch zurückgewiesene Datenbankzugriffe können protokolliert werden. Beispiele dafür sind auf der Konferenz-CD.
Sicherheitsmechanismen können in zweierlei Hinsicht implementiert werden. Zum einen kann man über die Open-Events verhindern, dass Benutzer auf Daten zugreifen, zu denen sie keinen Zugriff haben sollen. Zum anderen kann man die Datenbank über BeforeModify-Events vor unerlaubten Veränderung schützen. Beides geschieht, indem man den angemeldeten Benutzer in den entsprechenden Events prüft und die Ausführung ggf. stoppt. Beispiele dafür sind auf der Konferenz-CD.
Diese Vorgehensweise kann vor halbwissenden Benutzern schützen, die über eine VFP-Entwicklungsumgebung verfügen und somit Zugriff auf die Datenbank haben. Dann sollte man die Events in ein kompiliertes Programm auslagern, damit sie nicht auch einfach vom Halbwissenden geändert werden können. Wenn das Programm mit den Events ‚versehentlich' gelöscht wird, dann wird die Datenbank überhaupt nicht mehr geöffnet! Wie gesagt, dieser Schutz hilft nur gegen Halbwissende, denn der DBC kann immer noch als Tabelle oder mit einem Hex-Editor geöffnet werden. Aber dann muss man schon genau wissen, welches Flag zum Ausschalten der Events geändert werden muss.
In den meisten Firmen gibt es Standards, nach denen z.B. Tabellen benannt werden sollen. Oft sind auch für jede Tabelle bestimmte Felder zwingend vorgesehen, z.B. ein Timestamp für Angebotstabellen. Man kann nun in die ModifyAfter-Events Funktionsaufrufe einbaut, die diese Standards prüfen. Wenn die Standards nicht eingehalten wurden, kann man Warnungen ausgeben oder - vielleicht noch besser - diese erzwingen, indem man sie automatisch ausführt (also z.B. das Timestamp-Feld automatisch zur Tabelle hinzufügt).
Oft gibt es Projektteams, die nicht über eine zentrale Stelle zur Datenbankpflege verfügen. Dort arbeitet jeder Mitarbeiter auf einer lokalen Datenbankkopie, mit der er seine Änderungen zunächst testet, bevor er sie den anderen Entwicklern zur Verfügung stellt. Wenn der Entwickler nun seine Änderungen auf der zentralen Datenbank nachzieht, vergisst er oft, sämtliche Datenbankänderungen korrekt nachzuziehen. Wie schön wäre es da doch, hätte man ein Logbuch, in dem alle Änderungen eingetragen sind. Allerdings ist es derzeit noch nicht möglich, in den ModifyAfter-Events direkt festzustellen, was sich z.B. genau an einer Tabelle geändert hat.
Und wie erfahren die Kollegen meistens, dass sich die Datenbank geändert hat? Per Email, Mundpropaganda oder vielleicht, weil sich irgendein Programm wieder nicht ausführen ließ? Wenn man bei Datenbankänderungen gleich automatisch eine Email an die Kollegen verschicken läßt, dann werden diese Informationslücken geschlossen.
Man könnte z.B. nach dem Umbenennen von Tabellen prüfen, welche Programme auf diese Felder zugreifen und eine entsprechende Liste erstellen, welche Programme geändert werden müssen. In Anbetracht der Tatsache, dass meistens nicht nur ein Projekt mit einer Datenbank arbeitet und dass Umbenennungen von Feldern oder Views nicht so einfach festzustellen sind, stellt dies allerdings einen recht hohen Aufwand dar.
Diverse Anbieter von Datenbank-Tools arbeiten mit Meta-Daten, die aktualisiert werden müssen, sobald sich die Datenbank ändert. Zum Beispiel das Stonefield Database Toolkit von der Stonefield Systems Group. Das Stonefield Database Toolkit ermöglicht es, Datenbankänderungen an einer bestehenden Datenbank automatisch durchführen zu lassen. So kann man in die Datenbank in der Entwicklungsumgebung ändern, während der Benutzer bereits auf Produktionsdaten arbeitet. Die Änderungen werden in eine Metadatei geschrieben, über die dann die Datenbankänderungen abgeglichen werden.
Die Aktualisierung der Metadaten kann nun automatisch über DBC-Events angestoßen werden. Doug Hennig von der Stonefield Systems Group hat dazu die entsprechenden Events geschrieben und dankenswerter Weise zur Verfügung gestellt. Beispiele dazu sind auf der Konferenz-CD.
Beim Öffnen von Tabellen oder Views können nun datenbankgesteuert zusätzliche Aktionen ausgeführt werden. Zum Beispiel kann man einen View nach dem Öffnen mit einem Index versehen. Allerdings ist dabei zu beachten, dass beim REQUERY() von Views lediglich die Open-Events für die Tabellen gefeuert werden, die dem View zugrunde liegen. Also muss man anstatt REQUERY() aufzurufen, den View jedes mal wieder neu öffnen, wenn neue Daten gezogen sollen und ein neuer Index erstellt werden soll.