Zpracování výjimek s X++ and .NET Interop

Nedávná diskuze na komunitním fóru o zprávách ve výjimkách mě přiměla danou situaci důkladně otestovat a myslím, že výsledky stojí za zveřejnění. Tento příspěvek také zmiňuje něco, co jsem už zjistil dříve – že v kódu, který používá .NET Interop a který může být spouštěný jak jako X++ tak i CIL, může být zpracování výjimek celkem složité.

Pojďme si představit problém. Máme metodu třídy v X++ (AX2012), která vyhazuje výjimku:

public class XppClass
{
    public static void run()
    {
        throw error("It failed!");
    }
}

Je použita v .NET knihovně (za pomoci proxy třídy):

public class CSharpClass
{
    public static void Run()
    {
        try
        {
            XppClass.run();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

Tato .NET knihovna je opět volána z X++:

public static void main(Args args)
{
    try
    {
        CSharpClass::Run();
    }
    catch (Exception::CLRError)
    {
        info(AifUtil::getClrErrorMessage());
    }
    catch (Exception::Error)
    {
        info("Exception::Error zachycena");
    }
}

Zcela identický kód použijeme v různém kontextu – a uvidíme, že se nebude chovat stejně.

X++ kód na klientu

Pokud spustíme kódu na klientu, můžeme odchytit výjimku v X++ jako obvykle (tzn. infolog zobrazí “Exception::Error zachycena”). Výjimka v .NET kódu je typu Microsoft.Dynamics.AX.ManagedInterop.ErrorException a zpráva z infologu je obsažena ve vlastnosti Message:

ErrorException

To je to chování, které je popsáno v Proxies and Exception Mapping [AX 2012].

X++ kód na serveru

Nyní spusťme ten samý kód na serveru. Výjimku v X++ můžeme zachytit stejně jako předtím. Ale situace v .NET třídě je jiná – ačkoli typ je stále ErrorException, vlastnost Message obsahuje text “Exception of type ‘Microsoft.Dynamics.AX.ManagedInterop.ErrorException’ was thrown” namísto sktečné zprávy z infologu (a vnitřní výjimka je prázdná).

CIL

Když spustíme daný kód v CIL (například v dávkové úloze), situace je ještě odlišnější. Výjimka zachycená v .NET třídě je System.Reflection.TargetInvocationException (s obecnou zprávou “Exception has been thrown by the target of an invocation”). Její vnitřní výjimka je Microsoft.Dynamics.Ax.Xpp.ErrorException se stejně nicneříkající zprávou “Exception of type ‘Microsoft.Dynamics.Ax.Xpp.ErrorException’ was thrown”.

Výjimka v X++ je také TargetInvocationException a její vnitřní výjimka je ta, kterou jsme viděli v .NET. Protože je to .NETová výjimka, nemůžeme ji odchytnout pomocí catch (Exception::Error) (což je, mimochodem, přeloženo do CIL jako catch (ErrorException)), musíme použít catch (Exception::CLRError). To je velmi důležitý postřeh – pokud takový X++ kód spustíte v CIL, váš kód pro zpracování výjimek nemusí fungovat tak, jak očekáváte.

Závěr

Jak můžete vidět, kombinování .NET Interopu z X++ a .NET Interopu do X++ vám snadno může způsobit bolest hlavy, protože chování závisí na vrstvě (klient/server) a na tom, jestli kód běží jako X++ nebo CIL). V mnoha případech také nedostanete detaily o X++ výjimkách.

Měli byste to vzít v úvahu, když navrhujete vaše řešení – možná nepotřebujete používat .NET Interop z X++ a .NET Interop do X++ zároveň.

One Comment

  1. Co se týče “„Exception of type ‚Microsoft.Dynamics.AX.ManagedInterop.ErrorException‘ was thrown“ namísto sktečné zprávy z infologu (a vnitřní výjimka je prázdná)”, tak jsem na to narazil nedávno také. V mém případě to bylo tím, že výjimka byla vyhozena na jiné straně, než chytána – házela se na serveru, ale catch byl až na klientu. Když jsem to přeorganizoval, dostal jsem se korektně k chybové hlášce. Vyřešil jsem to úpravou AifUtil::getClrErrorMessage() tak, že pokud getLastException() vrátí null, zkusím ji zavolat ještě na té druhé straně.

Comments are closed.