Monday, 10 April 2023

Exception handling in X++ - D365 FO

This article describes the exception handling in X++. we can handle errors by using the  below key statements or keywords in the X++ code.

  • throw
  • try
  • catch
  • finally
  • retry

throw statements

We use the throw keyword to throw an Exception. throw statement can be used to either to throw generic error message or a custom error message. For example, the following statement throws an error exception.

throw Exception::error;

Instead of throwing an enum value, the below example shows the throw with a custom error message. The best practice is to use the output of the Global::error method as the operand for throw.

throw error("Error thrown.");  //The Hard coded values can also be replaced with labels created.

try, catch, finally, and retry statements

When an exception is thrown, it's first processed through the catch list of the innermost try block. If a catch block is found that handles the kind of exception that is being thrown, program control jumps to that catch block. If the catch list has no block that specifies the exception, the system passes the exception to the catch list of the next-innermost try block. The catch statements are processed in the same sequence as they appear in the code.

It's a common practice to have the first catch statement handle the Exception::Error enum value. One strategy is to have the last catch statement leave the exception type unspecified. In this case, the last catch statement handles all exceptions that aren't handled by any earlier catch statement. This strategy is appropriate for the outermost try...catch blocks.

An optional finally clause can be included in try...catch statements. The semantics of a finally clause are the same as they are in C#. The statements in the finally clause are executed when control leaves the try block, either normally or through an exception.

The retry statement can be written only in a catch block. The retry statement causes control to jump up to the first line of code in the associated try block. The retry statement is used when the cause of the exception can be fixed by the code in the catch block. The retry statement gives the code in the try block another opportunity to succeed. The retry statement erases all messages that have been written to the Infolog since program control entered the try block.

try

{

    // Code here.

}

catch (Exception::Numeric)

{

    info("Caught a Numeric exception.");

}

catch

{

    info("Caught an exception.");

}

finally

{

    // Executed no matter how the try block exits.

}

Exceptions inside transactions

If an exception is thrown inside a transaction, the transaction is automatically canceled (that is, a ttsAbort operation occurs). This behavior applies for both exceptions that are thrown manually and exceptions that the system throws. When an exception is thrown inside a ttsBegin-ttsCommit transaction block, no catch statement inside that transaction block can process the exception, (unless it is a UpdateConflict or a DuplicateKeyException). Instead, the innermost catch statements that are outside the transaction block are the first catch statements that are tested.

The finally clause will be executed even in transaction scope.

Examples of exception handling

The following code example shows exceptions in the Infolog

 

static void TryCatchGlobalError2Job(Args _args)

{

    /***

    The 'Global::error()' does directly add a message to the Infolog.

    The exception is caught.

    ***/

    try

    {

        info("In the 'try' block. (j2)");

        throw Global::error("Written to the Infolog.");

    }

    catch (Exception::Error)

    {

        info("Caught 'Exception::Error'.");

    }

 

/***  Infolog output

Message (03:51:44 pm)

In the 'try' block. (j2)

Written to the Infolog.

Caught 'Exception::Error'.

***/

}

 

Throwing an exception inside a transaction

The following code example throws an exception in a transaction block.

static void TryCatchTransaction5Job(Args _args)

{

    /***

    Shows an exception that is thrown inside a ttsBegin - ttsCommit

    transaction block cannot be caught inside that block.

    ***/

    try

    {

        try

        {

            ttsbegin;

            try

            {

                throw error("Throwing exception inside transaction.");

            }

            catch (Exception::Error)

            {

                info("Catch_1: Unexpected, caught in 'catch' inside the transaction block.");

            }

            ttscommit;

        }

        catch (Exception::Error)

        {

            info("Catch_2: Expected, caught in the innermost 'catch' that is outside of the transaction block.");

        }

    }

    catch (Exception::Error)

    {

        info("Catch_3: Unexpected, caught in 'catch' far outside the transaction block.");

    }

    info("End of job.");

 

/**********  Actual Infolog output

Message (04:12:34 pm)

Throwing exception inside transaction.

Catch_2: Expected, caught in the innermost 'catch' that is outside of the transaction block.

End of job.

**********/

}

 


1 comment: