Timers, Single Instance Codeunits, and NAS ServiceTier on Dynamics NAV 2013
There has been a time or two in the past (on older Microsoft Dynamics NAV versions and the old Navision Application Server) when I needed an automated process that would execute faster than the minimum one minute duration afforded to me by the Job Queue. For such cases, I would use a single instance codeunit and the NAV Timer (old-style COM automation technology).
The old Navision Application Server was the Classic Client without the GUI interface. For such a situation, I’d add code to the NASHandler function in Codeunit 1 ApplicationManagement … when the NAS service was added to Windows, it would be added with the parameter that would be hard-coded in CU1 to start a single instance codeunit. Within that codeunit, the NAV Timer would be started. Thereafter, as long as the NAS was running, the timer would fire the event trigger using the specified interval.
Recently, the same need arose for NAV 2013, but because of the incompatibility between the COM Automation and the new 64-bit servicetier, I needed the DotNet equivalent. I assumed without too much verification that the NAV Timer DLL was still using COM Automation and had not been upgraded to DotNet technology. I searched Mibuso and Freddy’s blog and found client-side add-ins for a NAV Timer to be added to Page objects, but I didn’t see an example for a timer to be started in a single instance codeunit designed to be started by the NAV 2013 servicetier.
With some experimentation, I found the solution. It is assumed the reader has in-depth knowledge of NAV development and can easily adapt the example shown below.
1. Create a codeunit. Set the property SingleInstance to Yes
Figure 1 – Codeunit with SingleInstance property set to Yes
2. Add the Timer to C/AL Globals, and be sure to set the property WithEvents to Yes
Figure 2 – Adding the Timer to C/AL Globals
Timer : DotNet "'System, Version=22.214.171.124, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Timers.Timer" WITHEVENTS;
3. Add these additional C/AL Globals
InProcess : Boolean; Started : Boolean;
4. Add the following code to the OnRun trigger
IF NOT Started THEN BEGIN Started := TRUE; CLEAR(Timer); Timer := Timer.Timer; Timer.Interval(5000); // fires every 5 seconds Timer.Start; END;
5. A new trigger was added when the WithEvents property was set to Yes … it will look like this: EVENT Timer::Elapsed … to this trigger add the following code:
IF InProcess THEN EXIT; // in case the process is not yet done with the prior iteration InProcess := TRUE; COMMIT; IF NOT CODEUNIT.RUN(CODEUNIT::YourProcessingCodeunitHere) THEN; // add a log entry somewhere InProcess := FALSE;
6. Finally, I create a new, single-purpose servicetier using the following command and the following relevant parameters in the CustomSettings.config file
Command sc create MicrosoftDynamicsNAVServer$Z binpath= "C:\Program Files\Microsoft Dynamics NAV \70\Service\Microsoft.Dynamics.Nav.Server.exe $Z" Parameters in CustomSettings.config <add key="ManagementServicesEnabled" value="false" /> <add key="ClientServicesEnabled" value="false" /> <add key="SOAPServicesEnabled" value="false" /> <add key="ODataServicesEnabled" value="false" /> <add key="NASServicesStartupCodeunit" value="ID of single instance codeunit" /> <add key="NASServicesStartupMethod" value="" /> <add key="NASServicesStartupArgument" value="" />
Word of caution: The process that is fired should not be a maintenance-intensive process or it will drag production performance to a crawl in some cases. Either the action performed should only happen once in a while even though the trigger is firing periodically (i.e., checking for an inbound receipt of data), or the action performed should be a very quick processing piece of code.
Be sure to document everything for posterity.