Exception handling in Axapta is a bit weird. Unlike C# or Java, exceptions aren’t full classes. In Axapta exceptions are defined in the enum Exception and that’s it. You can’t create your own exceptions and you can’t add data to the exception. If you want the error message you have to get it from the infolog. It’s limited but it usually doesn’t get in the way. Unless you add transactions to the mix.
The catch (no pun intended) is that the throw statement in Axapta also does an implicit ttsAbort if you’re in a transaction. And that’s where the confusion starts and computers get yelled at.
Suppose you’re doing some updates to several tables and need to stop processing when you detect an error. The simplified code would be something like this:
static void TryCatchOutsideTTS(Args _args) { ; try { ttsBegin; // ... throw error('Catch me if you can'); // ... ttsCommit; } catch (Exception::Error) { info('Gotcha'); } info('EOF'); } |
When you run this everything works as you’d expect:
So far so good. Now what about catching exceptions inside a transaction? A possible scenario is a loop to update records, logging errors and continuing with the next record when an error is encountered. The code boils down to:
static void TryCatchInTTS(Args _args) { ; ttsBegin; //while select forUpdate ... try { // ... throw error('Catch me if you can'); // ... } catch (Exception::Error) { info('Gotcha'); } ttsCommit; info('EOF'); } |
And this is what you get:
That’s strange. The catch block was not executed at all. Even worse, the part after the try/catch is ignored as well and the method ends immediately.
If you’re using a try/catch construct, you probably need to clean up whatever you’re doing if things go wrong. This shows there is no guarantee the catch block will be executed.
What if we add another try/catch?
static void DoubleTryCatch(Args _args) { ; try { ttsBegin; //while select forUpdate ... try { // ... throw error('Catch me if you can'); // ... } catch (Exception::Error) { info('Gotcha'); } ttsCommit; info('What about me?'); } catch (Exception::Error) { info('None shall pass'); } info('EOF'); } |
Which yields:
This behaviour surprised me at first but then I realized it’s the same as throwing a new exception inside a catch block. The ttsAbort makes it impossible to execute the rest of the transaction safely, even if part of it is outside the try/catch block. So the only option is to fall back to a higher catch block.
Usually this doesn’t matter. In some cases this can get in the way. Like when you’re using resources (open files, connections, …) you really should release when you’re done. Using resources in a transaction isn’t best practice but you could end up in that situation without realizing it. Whenever you reuse code, be it a standard API or a third party module, it could do things you’re not fully aware of.
There’s no simple solution to the problem. Just be careful and test thoroughly to make sure situations like this can’t bring down a production environment.
Thanks for this Information… I was really stuck with this crap behavior…. 🙂
Interestingly enough, I still can’t get this to work (although I removed the args and am calling it from another method).
I’m on 3.0, so not sure if that’s the issue.
Even taking your example (removing the Args args) and then wrapping it:
try
{
rjTestClass::DoubleTryCatch();
}
catch (exception::Error)
{
info(“i can’t believe this….”);
}
Still no dice.
Ronnie, I’m not sure I understand your problem.
When calling DoubleTryCatch your own catch clause should not be activated.
Were you expecting to see your own text in the info log?
This behaviour is the same in 3.0 and 4.0.
Great article. You’ve pointed out a very intricate point that I might’ve missed. Thanks for sharing.