26. Januar 2012 21:31
Dieser Artikel dient als Einstieg für C/AL-Programmierer, die bereits praktische Kenntnisse von Records besitzen.
Ziel ist es
nicht,
alles Wissenswerte aufzuzählen.
Kurze Wiederholung: RecordvariablenRecordvariablen erlauben uns in der C/AL-Programmierung den Zugriff auf Tabellen unserer NAV-Datenbank.
Deklariert ihr eine Recordvariable vom Typ Item (ich nenne sie an dieser Stelle mal
RecItem), so erstellt ihr damit eine Verknüpfung zur Artikeltabelle. In dieser Variable sind nun automatisch alle Tabellenfelder enthalten, die es in der Tabelle Item gibt.
Mit
RecItem könnt ihr nun Daten in der Artikeltabelle lesen oder auch diese ändern. Führt ihr z.B. ein
RecItem.MODIFY aus, so wird dieser Befehl direkt auf eurer Datenbank ausgeführt und der Artikel geändert.
Was sind temporäre Records?Temporäre Records unterscheiden sich auf den ersten Blick nicht von "normalen" Recordvariablen. Sie kennen auch die gleichen Befehle wie RESET, SETRANGE, INSERT, MODIFY etc.
Der Unterschied ist: Richtig angewendet, wird nicht in die Datenbank geschrieben, sondern in eine Art Zwischenspeicher. Wofür das gut sein soll, lest ihr
später.
Woran erkenne ich temporäre Records?Wenn der Programmierer sauber arbeitet, hoffentlich an ihrem Namen!
Es ist sehr ratsam (aber leider keine Pflicht), alle als temporär deklarierte Recordvariablen durch eine Namenserweiterung kenntlich zu machen. So könnte aus
RecItem ein
TmpRecItem oder
TempRecItem werden.
Meistens hält sich z.B. der Standard daran - leider aber auch nicht immer. Ein
TempRec kann in Wirklichkeit in "normaler" Record sein, und hinter einem
SalesLine könnte sich ein temporärer Record verbergen.
Das einzig sichere Kennzeichen ist daher die Deklaration.DeklarationIhr macht alles zunächst genauso wie bei gewöhnlichen Recordvariablen. Zum Schluss ruft ihr die Eigenschaften der Variable auf und setzt
Temporary = Yes.
Falls ihr die Eigenschaften nicht aufrufen könnt oder das Eigenschaftsfenster leer und nicht editierbar sind, speichert eure bisherigen Änderunge, schließt alle Fenster und versucht es erneut.Wofür das Ganze?Beispiel aus dem Standard:
Der Report 111 druckt die Debitoren-Top-10. Der Report muss, um dies drucken zu können, irgendwo eine Kombination aus Debitorennr. und Umsatz speichern. Die Debitorennr. ist vom Datentyp Code20 (also quasi Text), der Umsatz ist decimal. In C/AL haben wir zunächst keine Möglichkeit, diese Kombination zu speichern: Einzeln deklarierte Variablen stehen in keinem Zusammenhang zueinander. Auch Arrays (eine Variable mit gesetzter Eigenschaft Dimensions) bringen nicht die Lösung: Eine Arrayvariable kann nur mehrere Einzelvariablen des gleichen Datentyps beinhalten. Kombininiert ihr zwei Arrayvariablen, bleibt das Problem, das ein Array nur so viele "Zeilen" haben kann, wie ihr unter Eigenschaft Dimensions definiert habt.
Und was macht ihr, wenn ihr nicht nur zwei Variablen zusammen halten müsst, sondern noch viel mehr?
Im Fall des Reports 111 verwendet der Standard die temporäre Recordvariable CustAmount (die besser TempCustAmount genannt worden wäre). Dieser Record verweist auf Tabelle "Customer Amount", das ist Tabelle 266. Ihr werdet vielleicht feststellen, dass diese Tabelle in eurer Datanbank leer ist. Das liegt daran, dass es sich um eine sog.
Puffertabelle handelt, welche ausschließlich temporär verwendet wird.
Temporäre Records können aber auch verwendet werden, um sich anderweitig gefundene Datensätze (aus der gleichen Tabelle, versteht sich) zu merken und anderen Programmteilen zu übergeben. Die nächste Funktion kann zu der bereits gefüllten Recordvariable nochmal eigene Datensätze hinzufügen. Andere Funktionen löschen vielleicht wieder Datensätze aus diesem Pool heraus.
Der Inhalt temporärer Records lässt sich sogar auf Forms/Pages ausgeben.
Wie gehe ich mit temp. Records um?WICHTIG: Keine TriggerTrigger löst ihr aus, wenn ihr mit INSERT/MODIFY/DELETE/RENAME
(TRUE) oder auch
VALIDATE arbeitet.
Dann wird der Quelltext ausgeführt, der in der Tabelle an jeweiliger Stelle hinterlegt ist. Und das ist zu 99,99% schädlich.
Löscht ihr mittels DELETE(TRUE) beispielsweise einen temp. Auftragskopf, dann würden vielleicht echte (!) Verkaufszeilen in eurer Datenbank gelöscht werden. Denn die temporäre Eigenschaft vererbt sich nicht auf die Variablen innerhalb der Trigger.
Darum:
- Niemals!! Trigger an temporären Recordvariablen von Standardtabellen einsetzen.
- Wenn ihr die Tabellen selbst desingt, und ihr Trigger einsetzen müsst, verzichtet auf jeglichen Code in Triggern der die echte Datenbank modifizieren könnte!
- Verwendet für temporäre Records nach Möglichkeit nur Tabellen, die normalerweise keine Daten enthalten, dann macht es zunächst nichts, wenn TEMPORARY=Yes vergessen wird.
Eine frisch deklarierte temporäre Recordvariable verhält sich zunächst wie eine neu erstelle Tabelle: Sie ist komplett leer. Ihr müsst sie selbst per C/AL füllen.
Temp. Record befüllenBeispiel: Wir möchten alle (oder einige) Datensätze aus Tabelle 3 Payment Terms in einem temp. Records speichern.
Dann könnte der Quelltext so aussehen:
- Code:
IF PaymentTerms.FINDSET THEN BEGIN
REPEAT
TempPaymentTerms := PaymentTerms;
TempPaymentTerms.INSERT;
UNTIL PaymentTerms.NEXT = 0;
END;
Wie in "normalen" Tabelle auch würdet ihr übrigens einen Laufzeitfehler erhalten, falls ein Datensatz schon vorhanden ist.
Vielleicht möchtet ihr aber so eine Tabelle komplett manuell befüllen? Ein Datensatz könnte so angelegt werden:
- Code:
TempPaymentTerms.INIT;
TempPaymentTerms.Code := 'ZAHLCODE1';
TempPaymentTerms.Description := 'Zahlung sofort';
TempPaymentTerms.INSERT
Komplettes temp. Recordset leerenAngenommen, TempPaymentTerms zeigt noch immer auf ZAHLCODE1.
Führt ihr nun TempPaymentTerms.DELETE aus, so würdet ihr nur den ZAHLCODE1-Datensatz löschen.
Um wirklich alles zu löschen, gewöhnt euch am besten an:
- Code:
TempPaymentTerms.RESET;
TempPaymentTerms.DELETEALL; CLEAR(TempPaymentTerms);
(1) Löscht alle Datensätze, lässt aber die Felder auf dem zuletzt gezeigten Datensatz gefüllt.
(2) Initialisiert auch die zuletzt angezeigten Felder.
Am einfachsten lässt sich das nachvollziehen, wenn ihr diese Zeilen mal debuggt und die TempPaymentTerms-Variable beobachtet.
Zu guter Letzt: ein paar Tipps- Fühlt ihr euch fit im Umgang mit Records in C/AL? Wenn nicht, überlasst temporäre Records besser erfahreneren Programmieren.
- Temp. Records belasten den Zwischenspeicher. Gerät euer gefüllter Record zu "voll", kann dies euren Client ausbremsen.
- Ihr möchtet in eurer nächsten Codezeile TempRec.DELETEALL schreiben? Vergewissert euch aller-allerspätestens jetzt noch einmal, dass eure Variable korrekt als temporär deklariert ist!
- Vorsicht in den Reports für ungebuchte VK- und EK-Belegen (z.B. Auftragsbestätigung oder EK-Bestellung: Die globale Recordvariable SalesLine bzw. PurchLine ist - trotz ihres Namens - temporär! Wer - aus welchen Gründen auch immer - diese Variable auf nicht temporär setzt und den Report ausführt, löscht unwiderruflich alle VK- bzw. EK-Zeilen
Grund: SalesLine.DELETEALL bzw. PurchLine.DELETEALL im DataItem CopyLoop.
- Codeunit 5988 Serv-Documents Mgt. verwendet zahlreiche temporäre Records - nur leider sieht man das den Variablen dem Namen nach nicht an.