There are times where we might want to have a trigger which only runs under certain circumstances. There are a number of methods of handling these situations. This post will explain the various methods and when they should be used.
Some examples of when a conditional trigger might be used are:
- Applying code to Alternate windows:
Best practice is to never add your extra code directly to the alternate form. Just make the desired changes to the window layouts, but apply all code using focus triggers. This makes maintenance of the code much simpler, especially if the alternate window needs to be recreated when upgrading to a new version. This is explained further in the Knowledge Base (KB) article:
Rules to maintain alternate windows when you make customizations in Dexterity in Microsoft Dynamics GP (KB 929211)
However, if the alternate window is not selected by security, you need to make sure your triggers don't execute as they might cause "Illegal Address" errors if they refer to a field which was added to the alternate window and does not exist on the original window. For more information, see the following KB article:
Error message when you access new fields from an alternate window in Microsoft Dynamics GP: "Illegal Address for field 'field name' in script 'script name'" (KB 941327)
- Using the Three Trigger Technique:
The Three Trigger Technique is used to help place a triggering script in the middle of an original script, or to ensure that a trigger on a commonly used script only executes under specific circumstances. The concept and implementation is described in more detail in blog posts. The following is the first blog post in the series:
Using the Dexterity Three Trigger Technique Part 1
- Capturing a Table Buffer:
Capturing a reference to a table buffer is simple when you have access to the form's table buffers or if the table buffer is passed as a parameter, but when there is no easy method, it is possible to use a database trigger to capture the table buffer reference. The various techniques for this approach are discussed in the following blog post:
Accessing a Table Buffer when it is not passed as a parameter
- Enabling and Disabling functionality:
The last example is simply to enable or disable functionality based on settings in the application or product registration.
So, now we have a number of reasons why we might need to make triggers conditional, what are the methods to create conditional triggers?
The four methods for creating conditional triggers are listed below:
- Disabling all triggers for a product:
This method uses the Trigger_Enable()command to disable all triggers for the product. This method is "heavy handed" and is not recommended for a number of reasons, including: Once you have disabled all triggers for your product, you cannot use a trigger to turn your code back on; Also, this is a programmatic interface to the Customization Status window's ability to enable and disable triggers, this functionality is usually reserved for troubleshooting issues. In fact, the Dictionary Control feature of the Support Debugging Tool could re-enable the triggers when it is used for troubleshooting as it uses the same Dexterity command.
- Dynamically registering and unregistering triggers:
This method uses code to register the triggers and store the tag returned from the Trigger_RegisterXXX() command. This tag value is usually stored in a global variable or a memory table and later used with the Trigger_Unregister()command to unregister the trigger. This technique is unavoidable for on-the-fly triggers which can be different every time, such as those used by Field Level Security or the Support Debugging Tool's Advanced Debugging Mode.
However, if the trigger being registered is the same every time, one of the methods below is more efficient and simpler. Triggers registered in this method are not usually registered in the Startup script. This means that they cannot have the trigger sequence changed by re-ordering products in the DYNAMICS.SET launch file.
- Disabling individual triggers:
This method registers the triggers in the Startup script as usual and stores the tag values in global variables. The triggers are then disabled with the Trigger_DisableSingle() command until they are needed. Then they can be reactivated with the Trigger_EnableSingle() command and deactivated with the Trigger_DisableSingle() command when they are no longer needed. This leaves the trigger registered at all times and just controls when it is active.
This method is the preferred method for database triggers as an active database trigger on a table can prevent the table using SQL optimised commands. If there is a database trigger on a table, the Dexterity runtime will revert back to row by row ISAM style access allowing the trigger to fire for each record. Disabling the trigger will allow commands like range copy, sum range and remove range to be handled as single SQL statements.
This is the method used for example of capturing a table buffer above.
- Conditionally aborting triggers:
This final method registers the triggers in the Startup script, but does not need the tag stored. When the trigger fires, the code at the beginning of the script can check for the appropriate conditions and then issue an abort script command if the trigger is not needed. The trigger will always fire as it remains enabled, but it will only take actions when the conditions are met.
To avoid causing a performance hit when this trigger gets execute repetitively, it is best to use global variables rather than table access to check for the appropriate conditions. If this trigger is in the middle of a processing loop for a large number of records, the disabling method above might be more efficient.
This is the method usually used for the Three Trigger Technique or checking when alternate windows are in use.
In summary, the enabling and disabling of individual triggers method and the conditionally aborting method are the best methods to use. Dynamic or on-the-fly registration has its place, but enabling and disabling a product at a time is not recommended.
That's all for now.