AX2012: Events (II)

In the previous part I showed how to place a call of some action before or after a particular method. Today’s article describes another approach to events, much closer to events in C#, for instance.

Event-driven development is basically about having one part of an application raising an event (e.g. “button clicked” or “record modified”) and another part or parts of a program handling the event (i.e. running some code if the event occurs). The class raising an event has no information about processors of its events, which allows to decouple individual components.

A specification which method can be assigned to a specific event (and will be called when the event occurs) is defined by a delegate. Delegates in Dynamics AX 2012 defines number and types of parameters, allows to assign event handlers and allows to raise the event – compared with C# it is rather a mix of delegate and event constructs than delegate itself.

An event is raised by calling a delegate, as it would be a regular method. This activates event handlers previously assigned to this event.

Let’s demonstrate it on a simple example. Take a class simulating an automatic approval process, raising two events: approved and rejected. Use New > Delegate to create two delegates (notice the lightning icon in AOT), call them approved a rejected and let them accept a single parameter of type Approval.

public class Approval
{
    delegate void approved(Approval _sender){}
    delegate void rejected(Approval _sender){}
}

A delegate in AX2012 is always public and has a return type of void (because it can call several methods, it would potentially have several return values). Nevertheless it’s not a method and it cannot contain any code.

Regarding naming conventions and parameters, AX2012 is not too consistent. Most of delegates is defined with an “EventHandler” suffix (e.g. closedViewEventHandler), some use an “on” prefix (OnFileCreated) ans some use a logical name only (renderingCompleted). Even first letter casing is not unified. It would be useful if Microsoft defined some convention and respected it in its code.

By the way, notice that pairs of pre- and post- events (e.g. finalizingEventHandler and finalizedEventHandler) are commonly used also in AX.

Joris de Gruyter in his article about events in Dynamics AX recommends to use an object raising an event as the first parameter and an object extending XppEventArgs as the second parameter. Although this approach is consistent with .NET, I found in AX2012 the only delegate respecting the latter rule (namely SrsReportRunController.renderingCompleted), so it’s surely not the standard approach of developers of Dynamics AX 2012. Therefore I wouldn’t enforce it at the moment.

Back to the example. Let’s put to the Approval class a simulation of some business logic which raises the events. Notice that a delegate call is the same as a method call.

public class Approval
{
    delegate void approved(Approval _sender){}
    delegate void rejected(Approval _sender){}
    public void approve()
    {
        if (new RandomGenerate().randomInt(0, 1))
        {
            this.approved(this); //call the delegate
        }
        else
        {
            this.rejected(this); //call the delegate
        }
    }
 }

The class for approvals is thus finished and ready to use. Classes which will use it don’t have to bother with anything else than how (and whether) process the raised events. The following class uses the Approval class and process both of its events. Event handlers are assigned in the source code, although static handlers can be assigned in the AOT too.

public class Process
{
    public static void main(Args _args)
    {
        new Process().run();
    }
    public void run()
    {
        Approval approval = new Approval();
        //event handlers assignment
        approval.approved += eventhandler(this.doWhenApproved);
        approval.rejected += eventhandler(this.doWhenRejected);
        approval.approve();
    }
    public void doWhenApproved(Approval _approval)
    {
        info("Approved");
    }
    public void doWhenApproved(Approval _approval)
    {
        info("Rejected");
    }
}

The class is very simple, the only interesting thing is the assignment of handlers to events. Just as in C#, a handler is added by += operator and removed by -= operator. So there is no operator of simple assignment, which would overwrite existing handlers of the event. The right side of the assignment is composed of the eventhandler keyword and an identification of the method acting as an event handler. It must be public, must have the void return type and the same parameters as the delegate on the left side of assignment.

If an event has several handlers assigned in the code, their order is not specifically defined (I’ve experimentally verified that repeated calls of the same code can call handlers in different order).

If the handlers (from the previous example) were defined as static methods, the assignments to events would look like this:

approval.approved += eventhandler(Process::doWhenApproved);
approval.rejected += eventhandler(Process::doWhenRejected);

I hereby demonstrated the syntactic part of the thing, but the essential lies in new possibilities which events offer for object modeling. As many other novelties in AX2012, events require a certain change in way of thinking, but they provide an easier way for solving some type of problems and also allows to replicate some solutions and design patterns from other languages.