Try Catch and transactions

August 10, 2011 at 11:57
filed under Dynamics AX
Tagged , ,

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:

Transaction inside try/catch:

    try
    {
        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;

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.

    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 also 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 also 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 also 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.

3 comments

RSS / trackback

  1. AlexOnDAX

    Great post. One change I made to your demo code to make it a little more obvious is I changed the 3 info/warning messages to have the TTSLevel:

    warning(strFmt(“%1 can be caught inside transaction (TTSLevel: %2)”, exception, appl.ttsLevel()));

    warning(strFmt(“%1 can also be caught outside of the transaction (TTSLevel: %2)”, exception, appl.ttsLevel()));

    info(strFmt(“%1 can only be caught outside of the transaction (TTSLevel: %2)”, exception, appl.ttsLevel()));

  2. Trackbacks

respond