Eventing: an overview

A few days ago we had a workshop in the company about events. Even during our internal overview, we found out some thing which weren’t very clear before.

So I decided to try to list a bit about it here.

Types

Business

According to Microsoft the Business event type is meant to be defined once and basically never changed. There are actually no checks in place, but when you publish an event of type Business, you make a commitment that it’ll never change.

As far as I could see in the current NAV Version, even Microsoft hasn’t committed any event to never change.

Choosing the type business or integration will actually not have any influence on the way the event is handled.

Type Business is one of the 2 possible types to give to a custom event.

Integration

The Integration event type is the second of the 2 possible types of a custom event.

It’s purpose is to let other code “integrate” with your coding.

Triggers

Page Trigger events

A few actions on a page will automatically create page trigger events. This is an overview:

  • OnBeforeActionEvent
  • OnAfterActionEvent
  • OnAfterGetCurrRecordEvent
  • OnAfterGetRecordEvent
  • OnBeforeValidateEvent (this is called when a field loses focus and it’s value was changed)
  • OnAfterValidateEvent (same as above, but after page validate trigger)
  • OnClosePageEvent
  • OnDeleteRecordEvent
  • OnInsertRecordEvent
  • OnModifyRecordEvent
  • OnNewRecordEvent
  • OnPageOpenEvent
  • OnQueryClosePageEvent

Using these will allow us to execute code on a page, without putting code on the page object itself.

Database Trigger Events

Also creating a table will automatically create available events. These will be created:

  • OnBeforeDeleteEvent
  • OnAfterDeleteEvent
  • OnBeforeInsertEvent
  • OnAfterInsertEvent
  • OnBeforeModifyEvent
  • OnAfterModifyEvent
  • OnBeforeRenameEvent
  • OnAfterRenameEvent
  • OnBeforeValidateEvent (for every field. When subscribing, you first select validate event, another option to select the field will become available)
  • OnAfterValidateEvent (same as above, but after validation trigger of the field)

Global Events

Global events are predefined events that are raised typically in Codeunit 1. Examples for this are OnAfterCompanyOpen and GetSystemIndicator

Order of Execution

Code

When you change a record, it might be interesting to know in which order the code will execute.

Let’s assume we are doing Record.INSERT;
This is how it is specified by the help files:

  1. OnBeforeInsert (Table Trigger Event)
  2. Table Insert Trigger
  3. OnBeforeDatabaseInsert (Global event)
  4. DatabaseInsert trigger in codeunit 1 will execute
  5. OnAfterDatabaseInsert (Global Event)
  6. The record insertion itself will happen on the database
  7. OnAfterInsert (Table Trigger Event)

This list has 3 and 5 added, while the help files do not, but I thought it might be good to explicitly add them, since there are events for these.

Events

Let’s say we have 1 event published and multiple subscribers. There is currently no way to determine which event subscriber will be executed first. I heard someone say that they are currently executed based on naming. There is probably not a way to make sure it is, or to be sure it stays like this. Don’t try funky stuff to ensure your position.

Just know that if you have subscribed to an event, and your subscription is ok (meaning all parameters are correct, binding is ok), your subscriber will be executed.

Publishing events

When we create a new event ourselfs we have a few properties:

Event Type

Business or Integration event type. See the description above. Let’s say that we will mostly make events of type Integration.

IncludeSender

When we subscribe to our publisher, the Sender object will automatically be added to our signature ByVar. This allows to get access to the public functions of the object while we have the exact instance of the object.

GlobalVarAccess

This option is only available to event type Integration. It will allow the subscribers access to globally defined variables in the object. I will explain in “Subscribing to an Event” how to access them.

Triggering an event

After you create a custom event, all you have to do is to “Raise” it. This is simply done by calling the event publisher function.

Best Practices

In my opinion, it’s good practice to have custom events as local. They shouldn’t really be called from anywhere else than the object itself. It also makes finding the trigger point a lot easier when going through your code.

An event should not contain any code or locals.

Another good best practice is to never call a publisher from more than one place. You want subscribers to know what the event is. If there is need to subscribe to a publisher that is executing from multiple places, these should be done through the predefined trigger events.

Subscribing to an Event

Subscribing to an event is only possible in a codeunit.

EventPublisherObject

Here we can select an object where we know an event is published.

EventFunction

We can select the available events. These can be trigger events or custom events.

EventPublisherElement

When we select EventFunction of type validate or page action, we get an extra property. In this list we get an overview of possible element names.

  • In the page action, we get an overview of all defined actions.
  • In the page validate, we get an overview of all fields on the page
  • in the table validate, we get a list of all fields in the table.

EventSubscriberInstance

This property is a property on codeunit level. It can be:

  • Static-Automatic: NAV will automatically enable the subscriber and link it to the publisher you defined.
  • Manual: The subscriber will not be executed until you enable it through code with the BINDSUBSCRIPTION and disable it with the UNBINDSUBSCRIPTION

In most cases the Static-Automatic, which is the default, is the way to go.

Accessing Sender

When the publisher has the IncludeSender enabled, it will include the sender in the parameters of the subscriber.

Accessing Global Vars

Let me first start with saying that it is best to avoid accessing global variables. If they ever change, you might not notice when compiling, but your event subscriber will probably no longer work…

Anyway, how to do it. Actually pretty simple. First make sure the publisher has the property enabled, or it won’t work. Next in the subscriber, you add an extra parameter with the EXACT SAME name and data type. You need to do this manually for each global variable you wish to access. By setting it ByVar, you can also change them. Setting it ByVar is however not mandatory.

Again, you need to note that if the publisher object removes a global var you access, or changes it’s name, you will not get a compile error, but your subscriber will not get executed.

Best Practices

The subscriber functions should also be declared local. It is never a function that needs to be called manually.

Debugging

If we go through coding, we just see the publisher functions, but we do not have the option to use “Go to definition” to see subscribers. In the “Event Subscriptions” overview, we can see everything that is subscribed, so this is the way to go.

When actually debugging the code, the debugger just steps into the subscription, while maintaining the call stack. Debugging would be just like the function was called luckily.

Overview of event subscribers

NAV contains a virtual table with all event subscriptions. This can be accessed from the development client by going to Tools => Debugger => Event Subscriptions.

Another method of opening this page is by opening the Sessions page (also known as the “debugger”) from the windows client and clicking the “Event Subscriptions” button.

EventSubscriptions

In this page we see the subscriber information (Codeunit, Function) and the publisher information (Object ID, Function, Type).

The type of subscriber instance is also visible. But you have to note that if the instance is set to manual, the event subscription will only appear in the table as soon as it has been bound once. Another field shows how many times the subscriber has been fired.

Conclusion

Events are a really great tool to interact with the default coding of NAV without having to change the default coding.

The more default integration events become available, the more we can use them. This will result in easier upgrading. Let’s say we subscribe to Codeunit 80 events to do extra stuff, we don’t need to worry if codeunit 80 gets changed completely.

One big issue, which we do need to keep in mind, is “loosely binding” of events. We have the Event Subscriptions table where we can see if they are active, but we do not get compile errors, nor do the users get an error if the function is no longer bound. This could result in a lot of problems in a production database when some code is no longer run.
It is imperative to check the event subscriptions after changing coding, before putting code live. Perhaps in a next version, NAV will come with a better mechanism to detect these problems. But for now, keep checking that table, or build some smart mechanism yourself. 🙂

 

UPDATE: Please read this follow up post for a few important additions: https://nav-magno.be/2016/01/eventing-little-update/

9 thoughts on “Eventing: an overview

  1. Hi,

    I use events and also teh posibilitys of “Accessing Sender”. It works wonderfull. Now, I want to downgrade that solution to NAV 2013 – NAV 2015 were not events are possible.

    Is there a equal solution to get a sender-variable of a codeunit. I saw that datatype codeunit is also possible as a Parameter. But, how I get the a variable of the calling codeunit to call the function?

    “codeunit y”: which calls “codeunit z”:

    OnAfterBufferPosting-InventoryPostingToGL(????,ValueEntry,CostToPost,CosttoPostACY…);

    function in “codeunit z”:
    OnAfterBufferOutputPosting-InventoryPostingToGL(VAR Sender : Codeunit “Inventory Posting To G/L”;ValueEntry : Record “Value Entry”;CostToPost : Decimal;CostToPostACY : Decimal;ExpCostToPost : Decimal;ExpCostToPostACY : Decimal
    WITH ValueEntry DO BEGIN
    IF NOT “Production Cost” THEN
    EXIT;

    Sender.InitInvtPostBuf(…

    Maybe anybody has an idea?

    b rg
    Stefan

    • Hello Stefan,

      As far as I know, there isn’t a way of accessing the sender or sending the current codeunit object.
      You could work around this, by making the codeunit single instance. That way, you just declare it again in your code and would have the same instance.

      Regards,
      Magno

  2. If I make some modifications on a tabel. Do I have to re-compile the event codeunit for the tabel? We have had some issues with events not being executed. If we re-compile the event codeunit, the events works again.

    Br,
    Gustav

    • Hello Gustav,

      I would think no, but cannot say for sure how it will react.
      Just imagine you import an extension from a third party which uses extensions, you cannot compile them.
      Or even if Microsoft uses one of their own extensions, it would be “stupid” to have to look for all subscribers on that object and recompile them all.

      That said, If you subscribe to a field onvalidate event and rename the field or whatever, who know what will happen. I would think the subscriber would go to status error if it does not match…

      • Thanks for the answear.

        Every time we change some code/adding a new field. The events stop working. And the only thing we have to do to fix it is to compile them again. We don’t get any errors, it just ignore the code in the events.

  3. Hi Joachim,

    Thanks for the post, great job.
    We implemented recently all the custom code outside the standard using events,
    but sometimes events are not triggered,so we applied Nav 2016 cu11(Platform Update)but we still have the same issues, the only way to fix it, is to recompile the objects.
    Did we miss any events programming rule ?
    Did you experienced this issue ?
    Any help will be appreciated.

    Thanks;

    • Hello Salhi,

      I’m unaware of specific programming rules which would make the events not trigger.
      I would suggest first to check event subscriptions table, to see if there is an error or something there.

      Otherwise, it might be a good idea to open an incident with Microsoft for support on this.

      Magno

      • Hi Joachim,

        Thanks, We applied a new CU and We will keep an eye on that. I hope it will fix the issue.

        I will keep you updated.

        Bassem.

Leave a Reply