AX2012: Události (I)

Jedním z nových konceptů v Dynamics AX 2012 jsou události (events) a jejich zpracování (event handling). Dlouho dopředu bylo avizováno, že bude možné “navěsit” obsluhu události (event handler) před a za každou metodu, tak se pojďme podívat, co to znamená v praxi.

Terminologická poznámka: Event handler je v české literatuře obvykle označován jako “obsluha události” nebo “ovladač události”, mnohdy se ale používá prostě “handler”.

New Event Handler SubscriptionNa metodách tříd a tabulek lze z kontextové nabídky zvolit New Event Handler Subscription (platí pro instanční i statické metody, ne však pro display a edit metody). Tím je vytvořen poduzel dané metody, defaultně nazvaný EventHandler1.

Event Handler

Event Handler Subscription PropertiesEvent Handler Subscription definuje třídu a metodu, která bude spuštěna, zda bude spuštěna před nebo za obsluhovanou metodou a zda je volaná třída implementována v X++ nebo v .NET.

Je-li obsluha události spuštěna před metodou, má možnost číst a měnit vstupní parametry, post-event handler může číst a měnit návratovou hodnotu. V obou případech je navíc možné pracovat se stavem objektu (pokud jde o instanční metodu).

Jedna metoda může kombinovat pre- a post- události a může obsahovat více než jednu metodu zpracovávající událost daného typu. Takže je možné mít u jedné metody tři handlery spouštěné před vlastní metodou a dva handlery po jejím ukončení. Handlery jsou spouštěné sekvenčně a pořadí spuštění je dáno pořadím uzlů v AOT.

Handlery

Handler lze vytvořit zvolením New > Pre- or post-event handler z kontextové nabídky na třídě. To, co vznikne, je úplně normální metoda, takže ji můžete napsat též ručně, z šablony a podobně.
Handler je veřejná statická metoda s návratovým typem void (práce s návratovou hodnotou probíhá jinak, viz dále). Akceptuje jeden parametr typu XppPrePostArgs nebo má stejné parametry jako obsluhovaná metoda. Použití XppPrePostArgs je komplikovanější a znemožňuje kontrolu typu parametrů při kompilaci, ale zpřístupňuje řadu důležitých vlastností.

Deklarace nerozlišuje mezi pre-event a post-event handlery, takže jeden handler může být použit před i za obsluhovanými metodami. Pochopitelně ne všechny funkce mají smysl v obou kontextech – například nastavení návratové hodnoty nemá v pre-event handleru žádný efekt. Zda je handler volán jako pre- nebo post-event lze zjistit pomocí XppPrePostArgs.getCalledWhen(). (A osobně bych doporučoval dát jasně najevo, že je určitý handler určený jen pro jeden z případů, například pomocí Debug::assert(_args.getCalledWhen() == XppEventHandlerCallerWhen::Post);.)

Pre-event handler

Pre-event handler v AX2012 představuje metodu, která je volána před obsluhovanou metodou. Je volán po vyhodnocení hlavičky metody (nastavení defaultních hodnot nepovinných parametrů), ale před tělem metody.
Umožňuje číst a měnit parametry metody a pracovat se stavem objektu, například volat parm* metody.

Ukázka: deklarace handlerů

//obsluha metody run()
public static void runHandler() //stejné parametry jako run() = žádné
public static void runHandler(XppPrePostArgs _args) 
 
//obsluha metody run(int _iterace)
public static void runHandler(int _iterace) //stejné parametry jako run()
public static void runHandler(XppPrePostArgs _args)

Ukázka: Práce s parametry v XppPrePostArgs

public static void runHandler(XppPrePostArgs _args)
{
    //získání hodnoty - dle jména parametru
    int iterace = _args.getArgs('_iterace');
    //získání hodnoty - dle pořadí parametru
    int iterace = _args.args().valueIndex(1);
 
    if (iterace > 100)
    {
        //nastavení hodnoty parametru
        _args.setArg('_iterace', 100);
    }
}
Post-event handler

Post-event handler probíhá po skončení obsluhované metody, ale před vrácením hodnoty a řízení volajícímu kódu. Handler tak má možnost číst návratovou hodnotu (např. pro logování) a případně ji i měnit. Může také pracovat se stavem volajícího objektu (viz ukázku).

public static void validatePostHandler(XppPrePostArgs _args)
{
    SomeTable someTable;
 
    //Čtení návratové hodnoty
    if (_args.getReturnValue())
    {
        //Získání volajícího objektu
        someTable = _args.getThis();
 
        if (someTable.SomeField == '')
        {
            warning("Chybějící hodnota");
            //Nastavení návratové hodnoty
            _args.setReturnValue(false);
        }
    }
}

Debugging

Do obslužných metod můžete samozřejmě umisťovat breakpointy, ale můžete kód i krokovat a dostat se tak například do post-event handleru pomocí Step Into (F11) na uzavírací závorce obsluhované metody.

Závěr

Nejsem si jist, co byla hlavní motivace pro vytvoření této funkcionality, každopádně nám umožňuje zmenšit závislosti mezi objekty a provádět určité typy změn ve třídách či tabulkách beze změny jejich kódu. A kde není změna v kódu, nemůže nastat ani konflikt v kódu (konflikty na úrovni uzlů v AOT se řeší výrazně snáze).

Události v Dynamics AX 2012 toho nabízí ještě daleko více – pokusím se navázat na tento příspěvek co nejdříve.