Die dunkle Seite des Validate Triggers

Von | 2. September 2017

Hallo Leute,

heute möchte ich über ein sehr spezielles Thema sprechen. Vor einigen Jahren habe ich das erste Mal davon gehört und als mir ein Kollege dieses spezielle Verhalten von Dynamics NAV erklärte, konnte ich es nicht glauben. Ich erstellte mir direkt eine Testdatenbank und wollte es ausprobieren. Erstaunlicherweise konnte ich das Verhalten sofort reproduzieren und das in verschiedenen Dynamics NAV Versionen. An diesem Punkt realisierte ich, dass dieses Verhalten einen Grund haben muss.

Es handelt sich bei dem Problem um das Rollback Verhalten bei Programmierungen im Validate Trigger eines Tabellenfeldes. Wer das Thema kennt, braucht vermutlich nicht weiterlesen, aber wer jetzt glaubt, das dort gar kein Problem existiert, darf gerne folgendes Experiment mit mir versuchen. Dafür nutze ich eine Dynamics NAV 2017 CU 9 Cronus Demo Datenbank. Das ist zum jetzigen Zeitpunkt das aktuellste Build für Dynamics NAV 2017.

Stellen wir uns folgendes Szenario vor:

Beim Ändern des Feldes “Beschreibung” in der Verkaufszeile soll ein Eintrag in einer neuen Tabelle geschrieben werden. Dieser beinhaltet den Primärschlüssel der Verkaufszeile und eine laufende Nummer, sowie die Beschreibung. Jedes Mal, wenn die Beschreibung geändert wird, soll ein Eintrag geschrieben werden.

Mir ist dabei bewusst, dass es auch die Änderungsprotokollierung in Dynamics NAV gibt, aber die Funktion wollen wir in unseren Beispiel nicht verwenden. Wir öffnen also die Entwicklungsumgebung unserer Dynamics NAV 2017 Demo Datenbank und legen zuerst eine Tabelle an:

  1. Object Designer öffnen und neue Tabelle erstellen
  2. Wir legen diese Felder an: Primärschlüsselfelder der Verkaufszeile (Belegart, Belegnr., Zeilennr.), eine Lfd. Nr. als fortlaufende Nummer und das Feld Beschreibung:
  3. Die Tabelle weise ich der Nummer 50000 zu und vergebe den Namen “Description Log”
  4. Als Primärschlüssel verwenden wir folgende Felder: “Belegart”, “Belegnr.”, “Zeilennr.”, “Lfd. Nr.”

Mit der Tabelle haben wir die Grundlage zum Speichern von Daten. Zusätzlich erstellen wir noch eine Page zur Anzeige der Daten. Dafür führe ich folgende Schritte durch:

  1. Erstelle eine neue Page 50000 “Description Log List”
  2. Füge die Felder “Lfd. Nr.” und Beschreibung hinzu.
  3. Setze die Page auf nicht editierbar.

Nun füge ich die Page in die Actions der Verkaufsauftragssubpage hinzu:

  • Page 46 “Sales Order Subform” im Designer öffnen
  • Aktionen öffnen
  • Eine neue Aktion am Ende der Gruppe “Zeile” – “Verknüpfte Information” hinzufügen
  • Die neue Action mit der Page 50000 “Description Log List” verknüpfen

Im Verkaufsauftrag haben wir nun die Action zum Anzeigen des Beschreibungsprotokoll:

Als letztes bauen wir noch eine Anpassung in die Tabelle 37 im Feld “Beschreibung” ein um die Änderungen zu protokollieren.

Diese Anpassung dient nur der Demonstration und ist keine Empfehlung zur Umsetzung.  Daran können wir aber feststellen, was das eigentlich Problem ist. Ich ändere nun die Beschreibung von:

  • TOKYO Gästestuhl, blau” auf “TOKYO Gästestuhl, blau2
  • TOKYO Gästestuhl, blau2” auf “TOKYO Gästestuhl, blau1
  • TOKYO Gästestuhl, blau1” auf “TOKYO Gästestuhl, blau3

Es werden drei Einträge in unserem Beschreibungsprotokoll angezeigt und das ist auch mein gewünschtes Ergebnis. Anscheinend funktioniert alles prima! Nun programmiere ich einen Error in den Modify Trigger der Tabelle 37. Sowas kann man sehr gut vergleichen mit einer Prüfung, ob der Beleg überhaupt “Offen” ist (Beispiel “TestStatusOpen” Funktion). Solche Prüfungen sind in Tabellentriggern nicht unüblich. Zu Demonstrationszwecken nehmen wir hier einen harten Error ohne Bedingung.

Was passiert nun, wenn wir die Beschreibung ein weiteres Mal ändern:

  • Ich trage TOKYO Gästestuhl, blau4 in das Feld Beschreibung ein und drücke Enter. Ich stehe nun im Feld Menge.

Anschließend verlasse ich die Zeile, indem ich die Pfeiltaste nach unten drücke:

An dieser Stelle bekomme ich nun den Fehler “Stop”, da nun mit dem Verlassen der Zeile der OnModify Trigger der Tabelle aufgerufen wurde. Ich erwarte nun einen kompletten Rollback:

  1. Setze die Verkaufszeile auf “TOKYO Gästestuhl, blau3” zurück
  2. Schreibe keinen Eintrag in der Beschreibungsprotokoll Tabelle

Ich drücke nun F5 und löse den Fehler auf, indem ich meine Änderung verwerfe. Anschließend erhalte ich folgendes Ergebnis:

Wir können klar erkennen, dass der aktuelle Datensatz zurückgesetzt wurde und die Tabelle “Beschreibung Protokollliste” nicht zurückgesetzt wurde.

Was lernen wir daraus? Der Rollback auf der Zeile klappt wunderbar. Der Eintrag in meiner Protokolltabelle ist aber trotzdem vorhanden. Hier fand kein Rollback statt. Warum ist das so? Beim Validieren eines Feldes in einer Page wird der Validate Trigger der Tabelle durchlaufen. Solange der Cursor aber noch in der aktuellen Zeile stehen bleibt, wurde der OnModify Trigger noch nicht durchlaufen. Dynamics NAV speichert aber dennoch alle Änderungen an Fremdtabellen (in unserem Beispiel die Tabelle 50000 “Description Log”) bereits in der Datenbank. Verlasse ich nun die Zeile, wird nur der Rollback auf den aktuellen Record durchgeführt. Alle anderen Transaktionen auf andere Tabellen sind davon nicht betroffen, da diese bereits comitted sind.

Ich vermute, dass dies gewollt ist, damit die Transaktion nicht solange gesperrt wird, wie der Cursor auf dem Datensatz steht. Da zwischen einen Validate in einer Page und dem tatsächlichen Modify auf der Tabelle mehrere Minuten liegen könnten, ist das wohl der Kompromiss. Deswegen spreche ich von der dunklen Seite des Validate Triggers.

Als Faustregel kann folgendes abgeleitet werden:

  • Erstelle, ändere, lösche oder benenne keine Datensätze von anderen Recordvariablen in einem Validate Trigger einer Tabelle.
  • Füge den Code stattdessen in den OnInsert, OnModify, OnRename oder OnDelete ein. Hier funktioniert auch der Rollback auf anderen Record Variablen.

Ich hoffe das dieser Beitrag einigen Leuten hilft und sensibilisiert.

11 Gedanken zu „Die dunkle Seite des Validate Triggers

  1. Nadine

    Sollte dann nicht zumindest der rollback irgendwie im Protokoll vermerkt werden? Das Protokoll ist ansonsten nicht mehr nachvollziehbar.

    Guter Beitrag

    Antworten
    1. Robert Beitragsautor

      Das ist leider nicht möglich. Deswegen ist das ja auch so ein großes Problem, wenn in dieser Form programmiert wird. Das führt im schlimmsten Fall zu komplett falschen Daten.

      Antworten
  2. Bernd

    Hallo,

    zu “Füge den Code stattdessen in den OnInsert, OnModify, OnRename oder OnDelete ein. Hier funktioniert auch der Rollback auf anderen Record Variablen.”
    Dies steht dann in Widerspruch, dass Businesslogik und GUI getrennt werden sollten. Klar ist das schon auch weiterhin irgendwie machbar, aber schön ist anders.

    Antworten
    1. Robert Beitragsautor

      Mir ging es hier nur um das Aufzeigen dieses Problems. Lösungsmöglichkeiten gibt es hier sicherlich einige und mein Tipp ist ein Weg um das Problem zu lösen. Wie man vorgehen sollte, hängt sicherlich vom direkten Fall ab!

      Antworten
      1. Bernd

        Dann wäre ich geneigt von einem konzeptionellen Fehler zu sprechen. NAV ist derzeit noch alles andere als auf “dumme” GUI und Batchbetrieb ausgerichtet.

        Antworten
  3. Danny

    Mal wieder ein super Beitrag!

    Danke für die ausführliche Herleitung und grundsätzliche Erläuterung des Verhaltens.

    Antworten
  4. Mila

    The issue persists in BC19, CU03. Even if you change the Description on the Sales Line and press the down / up arrow (as in move away from the Record on the Page and have the OnModify run straight away).

    This happens with both OnBeforeModify and OnAfterModify events.

    Antworten
  5. Gerd Hübner

    Dieses Verhalten des “automatischen COMMITS bei Schreibbefehlen im OnValidate-Trigger” ist auch schon von den alten NAV-Versionen (z. B. NAV 2009 R2) her bekannt. Um Inkonsistenzen in den Daten zu vermeiden, setze ich daher gerne einen expliziten Rec.MODIFY(TRUE) am Ende des OnValidate-Triggers.

    Antworten
    1. Robert Beitragsautor

      Das ist sicherlich auch ein Workaround, aber dabei sollte man im Hinterkopf haben, dass der Modify trigger dann oft mehrmals durchlaufen wird (im Validate und später nochmal durch die Modifikation). Das kann negative Auswirkungen auf die Performance haben.

      Antworten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert