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:
Image may be NSFW.
Clik here to view.
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ň.