Hi all,
Exception handling can be quite confusing in Dynamics AX, so I wanted to share some quick facts about try/catch and transactions.
The general rule is that exceptions are caught in the outer most catch, where the ttslevel is 0. This means that if you put a transaction around a try/catch, your exceptions will not be caught.
The following two jobs demonstrate that:
The following two jobs demonstrate that:
Transaction inside try/catch:
try
{
ttsBegin;
throw error("an error");
ttsCommit;
}
catch
{
info("error caught");
}
{
ttsBegin;
throw error("an error");
ttsCommit;
}
catch
{
info("error caught");
}
Output:
Error an error
Info error caught
Try/catch inside transaction:
ttsBegin;
try
{
throw error("an error");
}
catch
{
info("error caught");
}
ttsCommit;
try
{
throw error("an error");
}
catch
{
info("error caught");
}
ttsCommit;
Output:
Error an error
As you can see, the error is not caught when the transaction is around the try catch.
However, there are two exceptions to this rule.
The following code demonstrates that UpdateConflict and DupplicateKeyException can be caught inside a transaction.
The following code demonstrates that UpdateConflict and DupplicateKeyException can be caught inside a transaction.
Set set = new Set(Types::Enum); // a set containing all possible exceptions
SetEnumerator se; // enumerator used to loop though the set with exception
Exception exception; // used to cast the value from the set
boolean caughtInside;
;
// add all exception to a set
set.add(Exception::Break);
set.add(Exception::CLRError);
set.add(Exception::CodeAccessSecurity);
set.add(Exception::DDEerror);
set.add(Exception::Deadlock);
set.add(Exception::DuplicateKeyException);
set.add(Exception::DuplicateKeyExceptionNotRecovered);
set.add(Exception::Error);
set.add(Exception::Info);
set.add(Exception::Internal);
set.add(Exception::Numeric);
set.add(Exception::PassClrObjectAcrossTiers);
set.add(Exception::Sequence);
set.add(Exception::Timeout);
set.add(Exception::UpdateConflict);
set.add(Exception::UpdateConflictNotRecovered);
set.add(Exception::Warning);
// create enumerator
se = set.getEnumerator();
// loop all exceptions
while(se.moveNext())
{
// set flag false
caughtInside = false;
// begin outer try catch
try
{
ttsBegin;
// begin inner try catch
try
{
// cast exception
exception = se.current();
// trhow exception
throw exception;
}
catch
{
// set flag to indicate the exception was caught inside the transaction
caughtInside = true;
warning(strFmt("%1 can be caught inside transaction", exception));
// throw exception again to catch it outside of transaction
throw exception;
}
ttsCommit;
}
catch
{
// for once, it's ok to catch everyting :)
if(caughtInside)
{
warning(strFmt("%1 can alse be caught outside of the transaction", exception));
}
else
{
info(strFmt("%1 can only be caught outside of the transaction", exception));
}
}
}
SetEnumerator se; // enumerator used to loop though the set with exception
Exception exception; // used to cast the value from the set
boolean caughtInside;
;
// add all exception to a set
set.add(Exception::Break);
set.add(Exception::CLRError);
set.add(Exception::CodeAccessSecurity);
set.add(Exception::DDEerror);
set.add(Exception::Deadlock);
set.add(Exception::DuplicateKeyException);
set.add(Exception::DuplicateKeyExceptionNotRecovered);
set.add(Exception::Error);
set.add(Exception::Info);
set.add(Exception::Internal);
set.add(Exception::Numeric);
set.add(Exception::PassClrObjectAcrossTiers);
set.add(Exception::Sequence);
set.add(Exception::Timeout);
set.add(Exception::UpdateConflict);
set.add(Exception::UpdateConflictNotRecovered);
set.add(Exception::Warning);
// create enumerator
se = set.getEnumerator();
// loop all exceptions
while(se.moveNext())
{
// set flag false
caughtInside = false;
// begin outer try catch
try
{
ttsBegin;
// begin inner try catch
try
{
// cast exception
exception = se.current();
// trhow exception
throw exception;
}
catch
{
// set flag to indicate the exception was caught inside the transaction
caughtInside = true;
warning(strFmt("%1 can be caught inside transaction", exception));
// throw exception again to catch it outside of transaction
throw exception;
}
ttsCommit;
}
catch
{
// for once, it's ok to catch everyting :)
if(caughtInside)
{
warning(strFmt("%1 can alse be caught outside of the transaction", exception));
}
else
{
info(strFmt("%1 can only be caught outside of the transaction", exception));
}
}
}
Output:
Info Info can only be caught outside of the transaction
Info Warning can only be caught outside of the transaction
Info Deadlock can only be caught outside of the transaction
Info Error can only be caught outside of the transaction
Info Internal can only be caught outside of the transaction
Info Break can only be caught outside of the transaction
Info DDEerror can only be caught outside of the transaction
Info Sequence can only be caught outside of the transaction
Info Numeric can only be caught outside of the transaction
Info CLRError can only be caught outside of the transaction
Info CodeAccessSecurity can only be caught outside of the transaction
Warning UpdateConflict can be caught inside transaction
Warning UpdateConflict can alse be caught outside of the transaction
Info UpdateConflictNotRecovered can only be caught outside of the transaction
Warning DuplicateKeyException can be caught inside transaction
Warning DuplicateKeyException can alse be caught outside of the transaction
Info DuplicateKeyExceptionNotRecovered can only be caught outside of the transaction
Info Timeout can only be caught outside of the transaction
Info PassClrObjectAcrossTiers can only be caught outside of the transaction
It is also noteworthy that, when you enter a catch block, there has been a implicit ttsabort, so the transaction has been rolled back. However this is not true for UpdateConflict and DuplicateKeyException when they were caught inside a transaction.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.