[ Teil 1 ] [ Teil 2 ] [ Teil 3 ]

Programmieren mit Visual FoxPro

von Manfred Rätzmann

Visual FoxPro 3.0, seit August '95 auf dem deutschen Markt erhältlich, bietet dem xBase-Entwickler viele neue Möglichkeiten und stellt gleichzeitig erhöhte Anforderungen an den Entwickler. Zwischen der Vorversion FoxPro 2.6 und Visual FoxPro liegt ein Generationensprung, der unter anderem dafür verantwortlich ist, daß zum derzeitigen Zeitpunkt (April '96)???, also fast ein Jahr nach der Markteinführung in den USA, einige der unter FoxPro 2.6 verfügbaren Tools noch nicht komplett auf Visual FoxPro umgestellt sind oder sich gerade im Beta-Test Stadium befinden. Zu umfangreich sind die Änderungen gegenüber der Vorversion, als daß sich bestehende FoxPro 2.x Applikationen einfach umstellen ließen.

Die Unterschiede zur Vorversion betreffen fast alle Bereiche der Entwicklung:

  • Die Einführung des DBC, der Datenbank-Hülle, die die Tabellen zusammenfaßt, ermöglicht Gültigkeitsregeln auf Satz- und Feldebene, das Festlegen von Defaultwerten pro Feld und die Erstellung von Insert-, Delete- und Update-Triggern.
  • Neu ist die Möglichkeit, updatable Views zu verwenden, also aus beliebigen Feldern der Basistabellen neue, virtuelle Tabellen zusammenzustellen, die dem Anwender oder dem Anwendungsprogramm eine änderbare Teilansicht der Grunddaten zur Verfügung stellt. Die Grunddaten können dabei aus lokalen, das heißt FoxPro eigenen Tabellen, oder entfernten, über ODBC verknüpften Tabellen einer anderen Datenbank stammen.
  • Visual FoxPro unterstützt objektorientiertes Programmieren. Neue Sprachelemente ermöglichen die Definition von Klassen, Erzeugen, Verwalten und Freigeben von Objekten, das Anlegen von Klassenbibliotheken und vieles mehr.
  • Die Erstellung der Anwendungsoberfläche hat sich gegenüber FoxPro 2.6 grundsätzlich gewandelt und ähnelt jetzt sehr der von Visual Basic bekannten Vorgehensweise. Die Masken zur Ein- und Ausgabe von Daten heißen jetzt Formulare. Alle Elemente in diesen Formularen liegen als Objekte vor, deren Eigenschaften eingestellt werden können, um das gewünschte Erscheinungsbild zu erhalten.
  • Neben den einstellbaren Eigenschaften ermöglichen die Objekte der Anwendungsoberfläche durch Angabe von Methoden eine anwendungsspezifische Reaktion auf eintretende Ereignisse. Dabei hat sich die Zahl der Ereignisse, auf die reagiert werden kann, gegenüber FoxPro 2.6 vervielfacht.

Natürlich kann man mit Visual FoxPro ähnlich prozedural entwickeln wie bisher mit FoxPro 2.6. Selbst die @SAY ... und @GET ... Befehle mitsamt dem dazu gehörenden READ haben aus Gründen der Abwärtskompatibilität überlebt und ermöglichen dem verzweifelten xBase-Programmierer Momente des glücklichen Wiedererkennens. Wer die Vorteile, die Visual FoxPro (VFP) bietet, aber konsequent nutzen will und obendrein überzeugt vom Sinn eines Wechsels zum objektorientierten Programmieren ist, muß sich den gestiegenen Anforderungen stellen und die lange, steile Lernkurve bezwingen. Dabei will ich mit der hier beginnenden 3-teiligen Artikelserie "Programmieren mit Visual FoxPro" Hilfestellung leisten.

Der erste Teil wird sich mit Aspekten der VFP-Datenbank befassen. Dabei werde ich auf die verschiedenen Möglichkeiten der VFP-Datenbankengine eingehen, also auf die neuen Feldtypen von Visual FoxPro, auf Gültigkeitsregeln und Trigger, freie und gebundene Tabellen sowie Local- und Remote-Views.

Der zweite Teil wird der objektorientierten Programmierung mit Visual FoxPro gewidmet sein. Die Basisklassen von VFP, visuelle und nichtvisuelle Klassen, Containerklassen mit ihren Eigenschaften und Methoden werden vorgestellt. Die Umsetzung von OO-Konzepten wie Aggregation, Assoziation, Vererbung und Polymorphie sowie der Aufbau eigener Klassenbibliotheken wird besprochen.

Der dritte Teil wird die Erstellung der Anwendungsoberfläche zum Thema haben. Dabei stelle ich die verfügbaren Oberflächenelemente vor, gebe einige Hinweise zum Eventhandling mit Visual FoxPro und beschreibe die Möglichkeiten der Fehlerbehandlung.

Gerade dem Einzelentwickler oder dem kleinen Entwicklungsteam wird es kaum mehr möglich sein, Anwendungen von Grund auf selbst zu entwickeln. Stattdessen wird er oder es auf Werkzeuge und Klassenbibliotheken von Drittanbietern zurückgreifen müssen und wollen und die gewonnene Zeit lieber in die eigentliche Anwendungsentwicklung investieren. Deshalb wird in diesem Artikel auch immer mal wieder über solche Werkzeuge und Bibliotheken gesprochen werden.

Die Datenbank

Eine xBase Datenbank, wie die von FoxPro 2.6, besteht aus Tabellen und den dazugehörenden Indexdateien. Einen Überbau, der die zusammengehörenden Tabellen zentral verwaltet, gibt es nicht. Der Entwickler muß in seinem Programm selbst dafür sorgen, daß Beziehungen geknüpft werden, neue Tabellensätze mit Standardwerten versorgt werden und daß zum Beispiel keine Lagerartikel gelöscht werden können, für die noch Bestellungen vorliegen. Eine solche zentrale Verwaltungsinstanz für die Tabellen einer Datenbank stellt Visual FoxPro nun mit dem DBC zur Verfügung. Die Abkürzung DBC steht für DataBase Container. Der DBC ist selbst eine xBase-Tabelle, in der alle die Angaben gespeichert werden, die im Tabellen- oder Index-Header nicht untergebracht werden können. Die einfachste Art, einen leeren DBC zu erzeugen, ist, im Projektmanager von Visual FoxPro auf der Seite "Daten" die Zeile "Datenbanken" zu aktivieren und dann den "Neu" Knopf anzuklicken. Den daraufhin erscheinenden Datenbankdesigner erreichen Sie auch, indem Sie mit CREATE DATABASE einen leeren DBC erzeugen und diesen anschließend mit MODIFY DATABASE zur Bearbeitung aufrufen. Ein Klick mit der rechten Maustaste innerhalb des noch leeren Datenbankdesigners bringt ein Kontextmenü auf den Bildschirm. Der Menüpunkt "Neue Tabelle ..." bringt uns dann in den Tabellendesigner.

Abbildung 1: der Tabellendesigner von Visual FoxPro

Ein Blick auf den Tabellendesigner zeigt alle Neuerungen, die Visual FoxPro beim Erstellen von Datenbanktabellen zu bieten hat, konzentriert auf. Zunächst fällt auf, daß ein Tabellenname vergeben werden kann, der unabhängig vom Dateinamen dieser Tabelle ist. Wenn die Tabelle geöffnet wird, so wird der Tabellenname als Aliasname benutzt. Ein Tabellenname kann maximal 128 Zeichen lang sein, was auch der fleißigste Tipper selten ausschöpfen wird. Diverse Eigenschaften der Tabelle wie auch einzelner Felder können vorgegeben werden und werden im DBC festgehalten.

Bevor wir jedoch zu diesen Eigenschaften kommen, lassen Sie uns einen Blick auf die Definition der Felder werfen. Feldnamen können jetzt ebenfalls 128 Zeichen lang sein. Visual FoxPro kennt einige neue Feldtypen, zum Beispiel binäre Zeichen- und Memofelder, die von der automatischen Codepage-Umsetzung ausgenommen sind, einen Feldtyp Datum/Zeit, der für Felder gewählt werden kann, die als Zeitstempel dienen, die numerischen Feldtypen Double und Integer sowie den Feldtyp Währung.

  • Die binären Zeichen- und Memofelder können zum Speichern von Strings genutzt werden, die keine Textstrings sind, also beim Umsetzen der Tabelle auf eine andere Codepage unverändert bleiben sollen. Sie bieten sich zum Speichern von verschlüsselten Paßwörtern, Hexadezimalwerten oder Codeteilen bzw. Ausdrücken an, die mit EVALUATE() ausgewertet werden sollen.
  • Der Feldtyp Datum/Zeit belegt genau wie ein Datum 8 Bytes. Sie können mit Feldern dieses Typs auch die gleichen Rechenoperationen durchführen, wie mit Feldern vom Typ Datum. Zu beachten ist lediglich, daß Datum/Zeit-Felder mit Sekunden rechnen, während die kleinste Einheit bei reinen Datumsfeldern nach wie vor der Tag ist. So ergibt zum Beispiel {14.04.96 22:10:13} - 1 den Datum/Zeit-Wert {14.04.96 22:10:12} während {14.04.96} - 1 den Datumswert {13.04.96} ergibt.
  • Für Tabellenfelder, die zu Rechenoperationen benötigt werden, sind die Feldtypen Double und Integer dem schon von FoxPro 2.6 bekannten Feldtyp Numerisch vorzuziehen. Während Felder vom Typ Numerisch vor der Rechenoperation intern umgewandelt werden, ist dies bei Feldern vom Typ Double oder Integer nicht mehr nötig.
  • Der Feldtyp Währung belegt wie ein Double-Feld 8 Bytes und hat ein festes Format mit 15 Vor- und 4 Nachkommastellen.

.NULL.

Visual FoxPro stellt eine neue Konstante zur Verfügung, die .NULL. Dies hat sich auch in der Felddefinition niedergeschlagen. Hier können Sie nämlich angeben, ob ein Feld einen .NULL.-Wert annehmen kann oder nicht. .NULL. bedeutet - es ist kein Wert vorhanden, unterscheidet sich also von einer 0 in einem numerischen Feld oder von einem Leerzeichen in einem Textfeld. Selbst ein Leerstring ist nicht gleich .NULL. sondern immer noch ein String. Durch die Verwendung von .NULL. können Sie abprüfen, ob zum Beispiel eine Preisangabe explizit auf 0 gesetzt wurde oder ob der Anwender einfach vergessen hat, den Preis einzugeben - also kein Wert vorhanden ist. .NULL. besitzt keinen Typ. Wenn Sie nach einer Zuweisung wie LEER = {} abfragen: ? type('LEER') so erhalten Sie die Ausgabe "D" wie Datum, nach der Zuweisung LEER = .NULL. ergibt die Abfrage: ? type ('LEER') immer noch "D" - der einmal eingestellte Feldtyp bleibt also erhalten.

Ein Vergleich zweier Werte, von denen der eine .NULL. ist, liefert immer .NULL. als Ergebnis. Da einer der Vergleichswerte einfach nicht vorhanden ist, kann auch kein Ergebnis des Vergleichs vorhanden sein - eigentlich logisch. Leider wird diese Logik nicht ganz konsequent beibehalten. So liefern EMPTY(LEER) und ISBLANK(LEER) den Wert .F., wenn LEER gleich .NULL. ist. Zum Test auf einen .NULL.-Wert sollten Sie die Funktion ISNULL() benutzen. Diese Funktion ist dann besonders wichtig, wenn Sie in Ihrer Tabelle Felder mitführen, die .NULL. sein können. Sätze mit .NULL.-Werten kann man nämlich nicht über LOCATE FOR <Feldname> = .NULL. finden. Auch der Vergleich, den der LOCATE-Befehl mit den Feldern der Tabelle durchführt, liefert .NULL. zurück, wenn einer der Vergleichswerte - in unserem Fall die Vergleichskonstante - .NULL. ist. Die richtige Konstruktion muß also lauten LOCATE FOR ISNULL(<Feldname>).

Feldeigenschaften

Jedem Feld einer DBC-gebundenen Tabelle in Visual FoxPro können Sie vier Eigenschaften und einen Kommentar mitgeben. Im einzelnen sind dies:

  • Gültigkeitsregel: Hier können Sie eine Regel in Form eines Ausdrucks hinterlegen, der angibt, wie der im Tabellenfeld eingetragene Wert auf Gültigkeit überprüft werden soll. Der Ausdruck muß einen Wert .T. oder .F. zurückliefern. Wenn ein Tabellenfeld zum Beispiel nur Datumswerte enthalten soll, die größer oder gleich dem aktuellen Systemdatum sind, könnten Sie dies über den Ausdruck <Feldname> >= date() abprüfen. Statt <Feldname> muß dabei natürlich der aktuelle Feldname angegeben werden. Komplexere Abprüfungen können auch in eigenen Funktionen durchgeführt werden, die bei Gültigkeit des abzuprüfenden Wertes ein .T. zurückliefern und ein .F., wenn der Wert im Tabellenfeld nicht gültig ist. Solche Funktionen werden am besten direkt in der Datenbank gespeichert (siehe "gespeicherte Prozeduren" weiter unten).
    Die Gültigkeitsregel wird sofort überprüft, sobald der Anwender das mit dem Tabellenfeld verbundene Eingabefeld verlassen will. Dies ist unabhängig von der Art der Pufferung (die verschiedenen Möglichkeiten der Pufferung beschreibe ich ebenfalls weiter unten) und gilt sowohl für Eingabemasken als auch für den BROWSE-Befehl. Durch das Festlegen von Gültigkeitsregeln auf Feld- oder Satzebene kann damit auch verhindert werden, daß über BROWSE oder durch andere Programme, die über ODBC auf die Tabelle zugreifen, nichtgültige Werte in ein Tabellenfeld geschrieben werden. Wenn Sie eine Gültigkeitsregel für ein Feld später nachtragen, so müssen alle bereits in der Tabelle gespeicherten Sätze dieser Regel entsprechen. Visual FoxPro prüft dies vor dem Speichern Ihrer neuen Regel ab und weigert sich, diese Regel zu speichern, wenn die Tabelle bereits Sätze enthält, die die Regel verletzen.
  • Gültigkeitstext: Hier kann ein Text hinterlegt werden, der ausgegeben wird, wenn der Eintrag in einem Tabellenfeld die zugehörige Gültigkeitsregel verletzt.
  • Standardwert: Diese Eigenschaft gibt an, welcher Wert bei einem APPEND BLANK in das Tabellenfeld eingetragen werden soll. Auch der INSERT-SQL Befehl trägt die Standardwerte in alle Tabellenfelder ein, die im Befehl nicht aufgeführt sind. Die Angabe eines Standardwertes ist immer dann wichtig, wenn Sie für das Feld eine Gültigkeitsregel angegeben haben, die leere Feldinhalte ausschließt. Ohne einen Standardwert, der der Gültigkeitsregel entspricht, könnten Sie sonst den Befehl APPEND BLANK nicht mehr nutzen, da dieser sich weigert, einen Satz an die Tabelle anzuhängen, wenn ein Feld des Satzes seine Gültigkeitsregel verletzt.
  • Überschrift: Die hier eingetragene Bezeichnung des Feldes wird unter anderem vom BROWSE-Befehl als Spaltenüberschrift benutzt. Die Formularassistenten von Visual FoxPro tragen diese Bezeichnung als Feldlabel in das Ein-/Ausgabeformular ein. Allerdings werden diese Label als Text, das heißt fest verdrahtet, im Formular eingetragen. Die Änderung einer Feldbezeichnung im Datenbankdesigner wirkt sich also nicht auf einmal erstellte Formulare aus. Wenn Sie das besser machen wollen als die Formularassistenten, so können Sie während des Ladevorgangs eines Formulars die aktuellen Feldbezeichnungen mit Hilfe des DBGETPROP-Befehls aus dem DBC auslesen und somit die Label Ihres Formulars aktualisieren. Aber hier greife ich vor - zurück zu den Feldeigenschaften.
  • Kommentar: Eigentlich keine Feldeigenschaft, aber immer sehr nützlich. Da Sie den Inhalt des Feldkommentars mit dem DBGETPROP-Befehl zur Laufzeit sowohl lesen als auch mit DBSETPROP verändern können, läßt sich der Feldkommentar auch für beliebige andere Zwecke verwenden.

Tabelleneigenschaften

Die im vorigen Abschnitt besprochenen Eigenschaften können für das einzelne Tabellenfeld im DBC hinterlegt werden. Ebenso können aber auch Eigenschaften angegeben werden, die für die ganze Tabelle gelten. Diese sind:

  • Gültigkeitsregel auf Satzebene: Ähnlich wie die Gültigkeit eines Feldwertes kann auch die Gültigkeit des ganzen Satzes überprüft werden, bevor dieser in die Datenbank geschrieben wird. Das ermöglicht die Überprüfung von Feldern, die voneinander abhängig sind. Eine solche Überprüfung von abhängigen Feldern ist auf Feldebene schwer zu machen, da die Prüfung erst sinnvoll durchgeführt werden kann, wenn alle Werte angegeben sind. Dank GUI und Mausbedienung wissen Sie als Entwickler aber nie, wann das der Fall ist - es sei denn, der/die Anwender/in will den gerade bearbeiteten Satz speichern. Deshalb führt Visual FoxPro die Prüfung der Gültigkeitsregel auf Satzebene direkt vor dem Speichern aus.
  • Hierbei ist jetzt aber die Frage der Pufferung wichtig. Wenn Sie, wie bisher unter FoxPro 2.6, mit einer ungepufferten Tabelle arbeiten, wird die Gültigkeitsregel auf Satzebene sofort nach einer Änderung an einem Tabellenfeld überprüft. Dies führt unter Umständen zu einem Konflikt, da eine solche Regel ja voneinander abhängige Felder überprüft, die alle erst mit den richtigen Werten versorgt werden müßen. Ein satzweises Puffern ist also in den meisten Fällen erforderlich, wenn Sie eine Gültigkeitsregel auf Satzebene verwenden wollen.
  • Gültigkeitstext: Auch für die Gültigkeitsregel auf Satzebene ist ein Text hinterlegbar, der ausgegeben wird, wenn die Regel verletzt wird. Dieser Text wird allerdings nur vom BROWSE-Befehl ausgegeben. Wenn Sie den Text auch aus Ihren Ein-/Ausgabeformularen heraus ausgeben wollen, müssen Sie ihn zunächst mit DBGETPROP() aus dem DBC in eine Variable auslesen.
  • Trigger: Zu den Tabelleneigenschaften gehören auch die INSERT-, UPDATE- und DELETE-Trigger. Dies sind Ausdrücke, die jedesmal ausgewertet werden, wenn ein Datensatz angehängt, verändert oder gelöscht wird. Die Trigger-Ausdrücke müssen .T. oder .F. zurückgeben. Abhängig vom Rückgabewert wird der Befehl, der den Trigger ausgelöst hat, durchgeführt (bei Rückgabe .T.) oder nicht (Rückgabe .F.). Triggerausdrücke rufen im allgemeinen in der Datenbank gespeicherte Prozeduren auf, die zum einen prüfen, ob die auslösende Aktion ausgeführt werden darf und zum anderen notwendige Folgeaktionen durchführen.

Trigger benutzt man häufig dazu, die referentielle Integrität (RI) sicherzustellen. Ein solcher Trigger verhindert zum Beispiel, daß ein Lagerteil gelöscht wird, für das noch Bestellungen gespeichert sind. Derselbe Trigger sorgt auch dafür, daß beim Löschen eines Lagerteils alle Bewegungsdaten ebenfalls gelöscht werden und so weiter. RI-Trigger aufzubauen, ist nicht trivial. Visual FoxPro bietet einen Assistenten für referentielle Integrität an, der den erzeugten Code in Form von gespeicherten Prozeduren in der Datenbank ablegt und im Trigger-Ausdruck der jeweiligen Tabelle diesen Code aufruft. Der von diesem Assistenten generierte Code ist ausreichend, wenn a) keine zusammengesetzten Schlüssel verwendet werden und b) die Triggeraktionen auf Kaskadierung (Weitergeben einer Lösch- oder Änderungsaktion an die untergeordnete Tabelle), Restriktion (Verhindern der Aktion) und Ignorieren beschränkt werden können. Zusatztools zu Visual FoxPro, wie das E-R??? Modelling Tool xCase bieten weitergehende Möglichkeiten an. So können von xCase erzeugte Trigger mit zusammengesetzten Schlüsseln umgehen. xCase bietet beim Löschen eines Satzes der übergeordneten Tabelle auch die Triggeraktion Nullify an, also das automatische Leeren des Fremdschlüssels in der untergeordneten Tabelle.

Indexarten

Visual FoxPro kennt vier Arten von Indizes: Primärindex, potentielle Primärindizes, eindeutige und einfache Indizes. Ein einfacher Index ist ein Index, wie er bisher aus FoxPro 2.6 bekannt war. Für einen einfachen Index wird keine Prüfung auf Eindeutigkeit vorgenommen. Einfache Indizes dienen hauptsächlich zur Unterstützung der Zugriffsoptimierung durch das Rushmore-Verfahren und zur Anzeige der Tabelleninhalte in bestimmten Sortierungen. Auch der "eindeutige" Index, bisher als "unique" bekannt und zur Sicherstellung der Abwärtskompatibilität unterstützt, wird nicht eigentlich auf Eindeutigkeit überprüft. Es wird lediglich bei mehrfachem Vorkommen eines Indexwertes nur das erste Vorkommen gespeichert und die weiteren ignoriert - ein Verfahren, dessen tieferer Sinn mir bisher verborgen geblieben ist.

Neu und wichtig sind die potentiellen Primärindizes. Diese werden jetzt nämlich tatsächlich auf Eindeutigkeit geprüft, und Visual FoxPro weigert sich, einen Datensatz in einer Tabelle abzulegen, der die Eindeutigkeit eines potentiellen Primärindex verletzen würde. Der als tatsächlicher Primärindex ausgewiesene Index ist also nur primus inter pares, jeder potentielle Primärindex kann zum tatsächlichen Primärindex gemacht werden.

Persistente Beziehungen

Beziehungen zwischen DBC-gebundenen Tabellen können im Datenbankdesigner bereits festgelegt und im DBC gespeichert werden. Beim Öffnen einer Datenbank werden diese Beziehungen aber nicht automatisch aufgebaut. Dies ist nämlich die Aufgabe eines Datenumgebungs-Objektes, das jeder Ein-/Ausgabemaske und jedem Report zugeordnet werden kann. Beim Eintrag von Tabellen in einem Datenumgebungs-Objekt werden die zwischen den Tabellen bestehenden und im DBC gespeicherten Beziehungen übernommen. Auch der unten beschriebene Ansichtsdesigner nutzt die im DBC gespeicherten Informationen und baut die Beziehungen zwischen den gewählten Tabellen automatisch auf. Neben den persistenten, also dauerhaften Beziehungen besteht nach wie vor die Möglichkeit, temporäre Beziehungen mit SET RELATION TO ... INTO ... aufzubauen.

DBC-gebundene und freie Tabellen

Die oben beschriebenen Feld- und Tabelleneigenschaften gelten komplett nur für DBC-gebundene Tabellen. Tabellen, die nicht an einen DBC gebunden sind, werden freie Tabellen genannt. Visual FoxPro kann solche freien Tabellen sowohl im alten, d.h. FoxPro 2.x-kompatiblen Format, als auch im neuen Format verarbeiten. Für freie Tabellen im alten Format gelten natürlich die neuen Feldformate, die längeren Feldnamen und anderen Eigenschaften nicht. Freie Tabellen im neuen Format können jedoch die neuen Feldtypen beinhalten. Lange Feldnamen, Gültigkeitsregeln, Feldbezeichnungen, Primärkeys und Trigger werden aber im DBC abgespeichert und stehen von daher für freie Tabellen nicht zur Verfügung. Sollte dies einmal erforderlich werden, helfen auch hier Zusatztools von Drittanbietern weiter. In dem oben bereits erwähnten E-R Modelling Tool xCase können freie Tabellen und DBC-gebundene Tabellen in einem Modell zusammengeführt werden, wobei RI-Code momentan??? ebenfalls nur für die DBC-gebundenen Tabellen generiert wird. In der für Juni '96 angekündigten??? Professional-Version soll RI-Code auch für die freien Tabellen generiert werden. Bei den für FoxPro verfügbaren Applikations-Generatoren, die ein eigenes Data-Dictionary mitführen, ist das seit jeher so und gilt auch für die Visual FoxPro Versionen. In Visual ProMatrix zum Beispiel, einem Applikations-Generator für Visual FoxPro, werden freie und DBC-gebundene Tabellen gleichberechtigt mit allen Eigenschaften und eigenem Integritätscode im eigenen DD geführt. Dafür hapert es hier noch!!! mit der Integration zwischen DBC und Data-Dictionary, da für die Arbeit mit Visual ProMatrix ein DBC eigentlich gar nicht gebraucht wird.

Ansichten (Views)

Eine Ansicht, oder View, ist eine virtuelle Tabelle, in der Daten aus einer oder mehreren anderen Tabellen zusammengefaßt sind. Eine Ansicht enthält nur die Felder der Basistabellen, die in der Definition der Ansicht vorgesehen sind. Datensätze der Basistabellen können zu einem Datensatz in der Ansicht gruppiert sein; ebenso ist es möglich, daß die Ansicht die Daten der Basistabellen in einer anderen Sortierung ausgibt. Die Basistabellen einer Ansicht können lokale, das heißt FoxPro-eigene Tabellen, oder entfernte, über eine ODBC-Verbindung erreichte Tabellen eines Datenbankservers sein. Eine vorher erstellte Ansicht kann selbst wieder Basistabelle einer weiteren Ansicht sein.

Bild 2: Der Ansichtsdesigner von Visual FoxPro

Zum Erstellen von Ansichten bietet Visual FoxPro einen Ansichtsdesigner an. Dort werden zunächst die Tabellen angegeben, die in die Ansicht einbezogen werden sollen. Aus diesen Tabellen können dann die benötigten Felder ausgewählt werden. Auf weiteren Seiten des Ansichtsdesigners können Sortierung und Gruppierung der abzurufenden Daten festgelegt werden. Auf der letzten, im Bild dargestellten Seite wird bestimmt, wie die Aktualisierung der Basisdaten durchgeführt werden soll. Aktualisierungen werden grundsätzlich nur gesendet, wenn das dafür vorgesehene Kontrollkästchen angekreuzt ist. Um Aktualisierungen senden zu können, muß für jede zu aktualisierende Tabelle ein Schlüsselfeld angegeben werden. Dieses Schlüsselfeld muß nicht unbedingt mit einem Index belegt sein (was sich aus Gründen der Zugriffsoptimierung aber anbietet) und muß nicht eindeutig sein. Die zu aktualisierenden Felder können ebenfalls einzeln angegeben werden.

Aktualisierungen werden wahlweise über einen SQL-UPDATE Befehl oder über einen SQL-DELETE mit anschließendem SQL-INSERT durchgeführt. Der Aktualisierungsbefehl kann vor dem Aktualisieren abprüfen, ob die Basisdaten zwischenzeitlich von anderen Anwendern verändert wurden. Diese Abprüfung erstreckt sich wahlweise a) nur auf die als Schlüsselfelder markierten Felder, b) auf die Schlüsselfelder und alle aktualisierbaren Felder oder c) auf die Schlüsselfelder und die tatsächlich geänderten Felder. Die Option, daß die Schlüsselfelder und ein Zeitstempel des jeweiligen Basisdatensatzes überprüft werden, ist nur bei Remote Views, also bei Ansichten von Daten eines über OBDC verbundenen Datenbankservers verfügbar.

Aus den Angaben im Ansichtsdesigner generiert Visual FoxPro das benötigte SQL-Statement, das zur Kontrolle auch angezeigt werden kann. Programmgesteuert kann eine Ansicht mit dem Befehl CREATE SQL VIEW <Name der Ansicht> AS SELECT ... FROM ... aufgebaut werden.

Client/Server-Programmierung

Ansichten werden vom Programm wie FoxPro-eigene Tabellen behandelt. Alle xBase-Befehle zum Navigieren innerhalb einer Tabelle, Ändern von Daten in einer Tabelle und Anhängen neuer Datensätze an eine Tabelle funktionieren auch für Ansichten. Es ist also ohne weiteres möglich, über eine Ansicht die Daten eines Datenbankservers mit xBase-Befehlen zu manipulieren. Allerdings sollte man dabei immer im Auge behalten, daß diese Daten auf anderen Wegen zur Verfügung gestellt werden als Daten aus lokalen Tabellen. Die Erstellung einer Client/Server-Anwendung mit Hilfe von Ansichten benötigt daher noch ein gehöriges Maß an Feineinstellungen, um zum Beispiel zu verhindern, daß mehrere Millionen Datensätze über das Netz geschickt werden, obwohl nur einige wenige gebraucht werden. Visual FoxPro bietet dazu unterschiedliche Hilfsmittel an. Das erste und wichtigste ist, daß Ansichten parametrisiert werden können. Mit vorangestelltem Fragezeichen können Speichervariablen in die Auswahlkriterien aufgenommen werden. "?KundenNr" veranlaßt Visual FoxPro vor dem Senden des Abruf-Statements an den Datenbankserver, die Variable "KundenNr" auszuwerten und deren aktuellen Inhalt in das zu sendende Statement einzufügen. Wenn die Variable "KundenNr" nicht definiert ist, fordert Visual FoxPro den Anwender vor dem Senden des SQL-SELECTs auf, einen Wert für "KundenNr" einzugeben. Normalerweise bestückt jedoch das Anwendungsprogramm die als Ansichtsparameter verwendeten Variablen, um nur eine begrenzte Auswahl an Daten abzurufen. Wenn sich der Wert für den Ansichtsparameter ändert, weil der Anwender nun zum Beispiel die Daten eines anderen Kunden sehen will, reicht ein Aufruf der REQUERY()-Funktion, um die Ansicht mit dem aktuellen Parameterwert neu erstellen zu lassen.

Bild 3: Der Verbindungsdesigner von Visual FoxPro

Entfernte Ansichten (Remote-Views) greifen über eine??? ODBC-Verbindungen auf eine andere Datenquelle zu. Zur Definition dieser Verbindungen bietet Visual FoxPro einen Verbindungsdesigner an. Dort werden Datenquelle, Benutzerkennung und Paßwort oder alternativ ein Connection String hinterlegt. Der Verbindungsdesigner ermöglicht darüber hinaus die Festlegung von Parametern, die die Verbindung steuern oder optimieren. So kann zwischen synchroner und asynchroner Verarbeitung gewählt werden, Warnmeldungen des Servers können ausgeblendet werden, der Batch-Modus entscheidet darüber, ob alle Ergebnisse eines gesendeten Statements, das mehrere SELECTs enthält, gemeinsam zurückgeliefert werden oder einzeln, sobald der erste Ergebniscursor verfügbar ist. Die Wahl "automatische Transaktionen" bewirkt, daß jedes gesendete SQL-Statement als eigene Transaktion aufgefaßt wird. Ohne automatische Transaktionen muß die auf dem Server mit dem ersten Befehl angestoßene Transaktion vom Programm selbst mit einem COMMIT oder ROLLBACK beendet werden. Bei der Erstellung eines Remote-Views können weitere Parameter angegeben werden, die die Performance beeinflussen. Dies ist zunächst die Anzahl der in einem Block abzurufenden Sätze (Fetch Size), die standardmäßig auf 100 eingestellt ist. Wenn 100 Sätze des Ergebnisses gefunden wurden, wird die Kontrolle an das Programm zurückgegeben, das diese ersten Sätze bereits anzeigen oder verarbeiten kann. Im Hintergrund wird der Ergebniscursor dabei mit weiteren Sätzen gefüllt. Die maximale Anzahl der zu übertragenden Sätze ist eine Sicherung, die eingesetzt werden sollte, solange Sie nicht ganz sicher sind, daß Ihr Programm an keiner Stelle ungewollt alle Sätze einer Tabelle aus der Serverdatenbank abruft. Da Serverdatenbanken im allgemeinen keine Memofelder à la xBase kennen, können Sie angeben, ab welcher Größe ein Feld als Memofeld betrachtet werden soll und ob Sie Memofelder eventuell getrennt abrufen wollen. Die Definition der Verbindungen und Ansichten werden??? im DBC gespeichert. Zur Laufzeit können Sie die eingestellten Werte mit DBGETPROP() auslesen und größtenteils auch mit DBSETPROP() neu setzen.

SQL Pass-Through

Neben dem View-Support ermöglicht Visual FoxPro den Zugriff auf Serverdatenbanken über die SQL Pass-Through-Technik. Mit SQLEXEC() kann jede gewünschte SQL-Anweisung an den Server abgeschickt werden. Das erlaubt einen direkten Zugriff auf einstellbare Servereigenschaften, Definition und Änderung von Tabellen auf dem Server und Zugriff auf gespeicherte Prozeduren des Servers. Ansichten und SQL Pass-Through können kombiniert werden. Zusammen mit der sehr schnellen, robusten Datenbankengine von Visual FoxPro machen sie Visual FoxPro zu einer äußerst leistungsfähigen Client/Server-Entwicklungsumgebung.

Gespeicherte Prozeduren

Im DBC werden nicht nur die erweiterten Definitionen für Tabellen und Felder, die benannten Verbindungen und die Definitionen der lokalen oder entfernten Ansichten abgelegt. Im DBC kann darüber hinaus jede benutzerdefinierte Prozedur oder Funktion hinterlegt werden, die immer dann zur Verfügung stehen muß, wenn die Datenbank geöffnet ist. Gespeicherte Prozeduren werden beim Öffnen der Datenbank automatisch in den Speicher geladen. Sie sollten sich deshalb auf Funktionen beschränken, die immer dann verfügbar sein müssen, wenn die Datenbank-Struktur oder die in der Datenbank gespeicherten Daten verändert werden. Dies trifft generell auf die Funktionen zu, die von den Ausdrücken aufgerufen werden, die die Gültigkeitsregeln für Tabellenfelder und Tabellensätze beschreiben. Die Funktionen, die von den UPDATE-, INSERT- und DELETE-Triggern aufgerufen werden, gehören ebenfalls als gespeicherte Prozeduren in die Datenbank. Verarbeitungsfunktionen Ihrer Anwendung sollten Sie jedoch nicht in der Datenbank speichern, da diese nur unnötig Hauptspeicherplatz belegen, wenn die Datenbank außerhalb Ihrer Anwendung geöffnet wird.

Pufferung, Satzsperre und Tabellensperrung

Wenn Sie in FoxPro 2.x ein Feld einer Datentabelle direkt mit einem Ein-/Ausgabefeld einer Maske verknüpft haben, mußten Sie selbst dafür sorgen, daß der Inhalt des Datensatzes vor dem Ändern gesichert wurde, um dem Anwender die Möglichkeit zu geben, seine Änderungen rückgängig zu machen. Die weitaus meisten FoxPro-Programmierer haben deshalb darauf verzichtet, direkt auf den Tabellen der Datenbank zu arbeiten. Stattdessen wurde mit Hilfe des SCATTER-Befehls der Inhalt des Datensatzes in Speichervariablen übertragen, die nach der Änderung mit GATHER zurück in den Datensatz geschrieben wurden. Diese Vorgehensweise hat sich nun erübrigt, da Visual FoxPro unterschiedliche Möglichkeiten der Pufferung anbietet. Die Felder einer gepufferten Tabelle können jetzt problemlos mit Ein-/Ausgabefeldern verknüpft werden. Jede Änderung, die der Anwender an den Daten vornimmt, landet nämlich zunächst im VFP-eigenen Puffer für diese Tabelle. Erst wenn über das Programm (oder das Befehlsfenster) der Befehl TABLEUPDATE() aufgerufen wird, werden die Daten in die Originaltabelle zurückgeschrieben. TABLEREVERT() verwirft alle Änderungen und aktualisiert den Puffer wieder mit den Originaldaten. Für jede Tabelle können Sie folgende Auswahl treffen:

  1. Keine Pufferung: Verhalten wie bisher unter FoxPro 2.x
  2. Pessimistische Datensatzsperre: Der Originaldatensatz wird gesperrt, bevor er in den Puffer übertragen wird. Dort kann er bearbeitet werden, ohne daß die Gefahr besteht, daß gleichzeitig ein anderer Benutzer Änderungen an diesem Datensatz vornimmt. Die Sperre erfolgt, sobald der Anwender beginnt, den Datensatz zu ändern. Wenn der Satz nicht gesperrt werden kann, gibt Visual FoxPro einen Hinweis aus. Ist der Satz gesperrt, können andere Anwender nach wie vor lesend darauf zugreifen. Sie erhalten jedoch immer die Daten im Ursprungszustand, das heißt ohne die Änderungen, die gerade an diesem Datensatz vorgenommen werden.
  3. Optimistische Datensatzsperre: In diesem Fall wird der Originaldatensatz sofort in den Puffer geladen und kann dort verändert werden. Die Sperre erfolgt erst beim Speichern der geänderten Daten. Dadurch können Aktualisierungskonflikte entstehen, die durch eine Fehlerbehandlungsroutine abgehandelt werden müssen. Mit GETFLDSTATE() läßt sich feststellen, ob und welche Feldwerte zwischenzeitlich verändert wurden und ob der Löschstatus des Satzes während der eigenen Bearbeitung verändert wurde. CURVAL() gibt den aktuellen Wert eines Feldes im Originaldatensatz an, während OLDVAL() den Wert des Feldes angibt, den es vor der eigenen Änderung hatte. Mit diesen Abfragen können Sie entscheiden, wie auf den Aktualisierungskonflikt zu reagieren ist. Abhängig von dieser Entscheidung können Sie die Aktualisierung entweder durch Setzen eines Zusatzparameters bei TABLEUPDATE() erzwingen oder Ihre eigenen Änderungen durch TABLEREVERT() verwerfen.
  4. Pessimistische Tabellensperrung: Jeder Datensatz wird gesperrt, sobald er bearbeitet wird. Wenn der Satzzeiger auf einen anderen Satz verschoben wird, wird im Gegensatz zur pessimistischen Datensatzsperre keine Aktualisierung der Originaldaten vorgenommen. Der Satz bleibt weiterhin gesperrt, bis mit einem TABLEUPDATE() alle bearbeiteten Sätze aktualisiert werden.
  5. Optimistische Tabellensperrung: Alle bearbeiteten Sätze bleiben im Puffer, bis mit einem TABLEUPDATE() die Aktualisierung versucht wird. Auch hierbei kann es zu Aktualisierungskonflikten kommen, wenn einer der Sätze zwischenzeitlich von einem anderen Benutzer verändert wurde. Die Aktualisierung wird durchgeführt, bis alle Sätze aktualisiert sind oder ein Aktualisierungskonflikt aufgetreten ist. Ein Aktualisierungskonflikt sollte auch hier mit einer entsprechenden Fehlerroutine behandelt werden. Mit der Funktion GETNEXTMODIFIED() können Sie vor der Aktualisierung prüfen, ob irgendwo ein Konflikt auftreten würde, und diesen im Vorhinein abhandeln.

Transaktionen

Wenn während der Aktualisierung mehrerer Sätze ein Konflikt oder ein anderer Fehler auftritt, so sind die Originaldaten der vorher aktualisierten Sätze bereits geändert. Gesetzt den Fall, Ihre Fehlerbehandlungsroutine ermöglicht dem Anwender den Abbruch des Aktualisierungslaufs oder kann aus anderen Gründen den Konflikt nicht lösen und die Aktualisierung fortsetzen, so enthält Ihre Datenbank im schlimmsten Fall einen inkonstistenten Datenbestand. Um das zu vermeiden, können Sie Ihre Aktualisierungsoperationen als Ganzes schützen, indem Sie diese als eine Transaktion durchführen. Dazu setzen Sie vor die Aktualisierungsoperationen den Befehl BEGIN TRANSACTION und schließen die Aktualisierungsoperationen mit einem END TRANSACTION ab. Wenn nun zwischendurch ein nicht behebbarer Fehler auftritt, können Sie alle bis dahin vorgenommenen Änderungen mit ROLLBACK rückgängig machen. Da Datenbankserver eigene Transaktionsschutzmechanismen haben, gelten diese Befehle nur für FoxPro-eigene Tabellen. Im Umgang mit Datenbankservern können Sie entweder die automatischen Transaktionen verwenden (siehe dazu weiter oben "Client/Server Programmierung) oder mit SQLCOMMIT(), dem Pendant zu END TRANSACTION, die mit dem ersten SQL-Statement begonnene Transaktion abschließen. SQLROLLBACK() macht auch hier alle bis zu dem aufgetretenen Fehler durchgeführten Aktualisierungen wieder rückgängig.

Wie geht's weiter?

In der nächsten Ausgabe??? werden wir die zweite große Hürde angehen, mit der Visual FoxPro die xBase-Gemeinde konfrontiert hat: die objektorientierte Programmierung. Ich möchte Ihnen zeigen, wie die Konzepte der objektorientierten Programmierung in Visual FoxPro verwirklicht wurden und welchen Nutzen Sie als Anwendungsentwickler daraus ziehen können.

[ Teil 1 ] [ Teil 2 ] [ Teil 3 ]