Exception handling with X++ and .NET Interop
A recent discussion on the community forum about exception messages prompted me to test the given case thoroughly and I think the results are worth sharing. The post also mentions something what I already found before – that error handling can be tricky in code which uses .NET Interop and which can be executed as both X++ and CIL.
Let’s introduce the problem. We have an X++ class method (AX2012) that throws an exception:
public class XppClass { public static void run() { throw error("It failed!"); } }
It’s used in a .NET library (through a proxy class):
public class CSharpClass { public static void Run() { try { XppClass.run(); } catch (Exception ex) { throw; } } }
The .NET class is called from X++ again:
public static void main(Args args) { try { CSharpClass::Run(); } catch (Exception::CLRError) { info(AifUtil::getClrErrorMessage()); } catch (Exception::Error) { info("Exception::Error caught"); } }
We’ll use exactly the same code in different contexts – and we’ll see that the behavior won’t be the same.
Client-bound X++
If we run the code on client, we can catch the exception in X++ as usual (i.e. “Exception::Error caught”will be shown in infolog). In the .NET class, the exception object has typeMicrosoft.Dynamics.AX.ManagedInterop.ErrorException and the infolog message is contained in theMessage property:
That’s the behavior described in Proxies and Exception Mapping [AX 2012].
Server-bound X++
Now let’s run the same code on the server tier. The exception can be caught in X++ code as before. But the situation in the .NET class is different – although the type is still ErrorException, the Messageproperty contains text “Exception of type ‘Microsoft.Dynamics.AX.ManagedInterop.ErrorException’ was thrown” instead of the actual infolog message (and the InnerException is empty).
CIL session
When running the code in a CIL session (such as in batch), the situation is even more different. The exception caught in the .NET class is System.Reflection.TargetInvocationException (with a generic message “Exception has been thrown by the target of an invocation”). Its inner exception isMicrosoft.Dynamics.Ax.Xpp.ErrorException with an equally useless message “Exception of type ‘Microsoft.Dynamics.Ax.Xpp.ErrorException’ was thrown”.
The exception in X++ is TargetInvocationException too and its inner exception is the exception we saw in .NET. Because it’s a .NET exception, it won’t be caught by catch (Exception::Error) (which is, by the way, translated to catch (ErrorException) in CIL), we have to use catch (Exception::CLRError). That’s a really important observation – when you run such an X++ code in CIL, your error handling code may not work was expected.
Conclusion
As you can see, combining .NET Interop from X++ and .NET Interop to X++ may easily cause you a headache, because the behavior depends on execution tier and whether the code runs as X++ or CIL. In many cases, you also won’t get details about X++ exceptions.
You may want to take that into account when designing your solutions – maybe you don’t need to use.NET Interop from X++ and .NET Interop to X++ in the same time.
No comments:
Post a Comment