-->

X++ - Cancelling the event / method in its Pre event handler

maqk® by Unknown | 4:44 PM

Eventing enables to extend applications as well to decouple the components and underlying implementations. It is a great way to write new logic without worrying a change in the underlying components.

Automatic Event Handlers

X++ provides custom delegates as well as Automatic event handlers
  • Pre - Occurs before
  • Post - Occurs after

Pre Event Handler

One of the responsibilities / usage of the Pre event handler is to validate if all conditions are correct for the commencement of the main method. Or to populate / fill any other component(s) which is required in the main method.

Cancel Event

This became a pain for me last night. I was trying to do something that we does in DotNet windows application :D 

e.Cancel = true;

Where is that ? Answer is, perhaps no where :P (educate me if I am wrong please)

But there are alternates to it. Thanks to my ex colleague and friend Asim Saeed's blog post: Yes it is quite a natural alternate, simply throw an exception.

Problem

But that will end the whole program execution, unless caught in any catch and taken care there. What I mean is, consider a complex logic that spans multiple classes or methods. What if one of the optional method's Pre event handler simply throw's exception whereas the process should have continued. In such case, that particular event should be cancelled rather than the whole program flow. I want to have granular control here, only cancel the specfic event producer method, not the whole process 

One way to do this is to have a flag parameter in the event producer method's parameters, set it to false in the Pre event handler on particular condition (i.e. if things are not found good to continue this event). Then at the start of event producer method, simply return out if the status flag is found to be false.

To demonstrate the same, we have 2 classes,

  • CMaqkProducer_Event
    • With a method and its event handler subscription
  • CMaqkConsumer_Event
    • With event handlers registered to handle event subscription defined on the producer

The code for the producer class is provided for our reference;

Producer class: Maq_EventProducer




class Maq_EventProducer
{
    #PC
    //Macros for Product Configuration, carries #true and #false for sting to bool conversions
}

/// 
/// This method is producing a Pre event handler
/// 
/// 
/// A sample parameter passed to the method for
/// 
/// 
/// Status parameter passed from XppPrePostArgs class containing the result from Pre event Handler
/// 
/// 
/// We will return in the begining of this method if the results form Pre event Handler are not ok
/// 

public void method1(str _str, str _status = "")
{

    if (_status == #false)
        return;
    // continue; ;)
    info("Maq_EventProducer.method1() called");

}

/// 
/// Another method besides method1() 
/// 
/// 
/// This method exists and is called to demonstrate continuation of program flow besides event cancellation
/// 

public void method2()
{
    info("second method, Maq_EventProducer.method2() called");
}


Notice the line 'return;' at the begining of method1() after evaluating the _status defaulted value (string) value with #true. Using this theme, we cancel the whole event from progress.

Consumer Class: Maq_EventConsumer


class Maq_EventConsumer
{
    #PC
    //Macros for Product Configuration
    
    Maq_EventProducer   objProducer; //Reference to producer
}


/// 
/// parm method to access producer
/// 
/// 
/// the reference variable to the event producer class object
/// 
/// 
/// 
/// 
/// 
/// 
/// 

public Maq_EventProducer parmObjProducer(Maq_EventProducer _objProducer = objProducer)
{
    objProducer = _objProducer;

    return objProducer;
}

/// 
/// The static main method
/// 
/// 
/// arguments passed
/// 
/// 
/// 
/// 

public static void main(Args args)
{
    Maq_EventConsumer objConsumer = new Maq_EventConsumer();
    Maq_EventProducer objProducer = new Maq_EventProducer();
    objConsumer.parmObjProducer(objProducer);
    objConsumer.run();
}



/// 
/// the default logic processing method of consumer class
/// 
/// 
/// 
/// 

public void run()
{
    info("Consumer class run() method called");
    this.parmObjProducer().method1("Maqk");
    this.parmObjProducer().method2();

}


/// 
/// The pre Event Handler
/// 
/// 
/// payload
/// 
/// 
/// 
/// 

public static void preClass1_Method1(XppPrePostArgs _args)
{
    info(strFmt("pre event handler called"));

    // Cancelling the event by setting the function's parameter, "_status" = 0
    _args.setArg("_status",#false);

    // other code logic body may go here
    // other code logic body may go here

    // Checking if some logic has asked to cancel the event
    if (_args.getArg("_status") == #false)
        warning("Class1 method1() event cancelled due to condition");

    // Later in the event producer method body, we will check the value and would work accordingly if found 0

}

Result


Calling the consumer class's run() method calls both the methods of producer class. The snapshot below shows the program flow result.

Results - Program flow continues only canceling the event

The Pre Event Handler cancels the main event, but the program flow continues. Had we thrown error / warning in the Pre event handler, producer's method2() would not have been called. But now using the event payload (XppPrePostArgs) and having a default value parameter theme, we can only cancel the event we want to and not the whole program flow.
top