Great content delivered right to your mailbox

Thank you! Check your inbox for our monthly recap!

When we write code or make customizations to Dynamics 365 for Operation, which uses X++, we should make use of exception handling to provide some context for the message or a different, more useful message.

In this article, I will explore how to develop a uniform way to catch the multiple types of exception that can be raised in X++.

Learn more on how to manage your business with Dynamics 365

 

Exception Type

There are several types of exception and the type differs, depending on what caused the error. Many exception types are determined by the kernel and are not normally thrown by application code.

All exception types, however, can be caught, and it is the developers’ responsibility to decide which exceptions need to be handled.

The exception type is identified using the system-based enumeration called an exception. Because it is a system Enum, it cannot be modified, so users cannot add new exception types.

The following table shows the exception literals that are the values of the exception enumeration.

Exception Type

 

Click here to sign up to our partner program and start reselling Dynamics 365 in less than 10 minutes

 

Key Commands

  • Try command
    • signifies the start of a block of code that you want to control with the X++ exception handling system. Any exceptions that are thrown in that block of code can be caught and handled accordingly. The block of code inside the Try statement must be contained between brackets ( { } ).
  • Catch statements
    • come after the block of code and define what code is executed when each exception is thrown. You do not have to define a Catch statement for every possible exception; however, each Try statement must have at least one Catch statement.
  • Retry command
    • tells the system to go back to the Try statement and attempt to execute the code again. Any data that was loaded before the Try command will remain as it was, but any data retrieved or modified after the Try statement will be refreshed. When a deadlock exception is thrown, all locks on tables that this process holds are released, which may allow another process or processes that are also deadlocked to continue. By calling a retry, the process can attempt to update the record again and may now be able to complete. It is a best practice that a retry uses a counter so that the number of retries can be controlled and a user does not become stuck in a loop.
  • Final keyword
    •  is now available to follow the Try and Catch keywords.The semantics are identical to the semantics in C#. The statements provided in the final clause are executed, whether or not the Try block threw any exceptions.

 

Code Statement

We will use the following example lines of code to test exception handling:

codeCustCreateCust

 

This code will try to create a customer after inputted value from users is revised; it will also handle errors when the user does not input enough information. A Throw statement is used to throw an error that can be caught by a Catch statement. When the system throws an exception, ttsabort is called automatically, and so does not have to be called in a Catch statement.

 

Optimistic Concurrency Exceptions

The optimistic concurrency check (OCC) is a performance-enhancing function within Microsoft Dynamics 365 for Operation. It presumes that any record retrieved from the database is not updated until it is proven to be updated by the database. This means that fewer locks must be placed on records in the database. This allows for faster access for other users.

This also means that one user can update a record after another user has retrieved it from the database. This can cause data inconsistency. If the second user then also tries to update the record, an UpdateConflict exception is thrown.

The system does this by comparing the recVersion field on the record buffer at runtime and the actual record in the database. The recVersion field value is changed every time that an update is successfully made to a record.

There are two main table update exceptions, UpdateConflict and DeadLock. An update conflict occurs due to the optimistic concurrency failing, whereas a deadlock is the classic database scenario where each transaction has locked a table that the other needs.

Update conflicts are normally handled within the insert, delete, and update methods of a table. The BOM table is a good example of this. It is hard to find many examples where this has been used.

We use this pattern only if we deem it to be required. The code within the table’s update method also updates other records, so it has been written to handle update conflicts.

The following code is an example of how to handle the UpdateConflict exception that might be thrown.

CodeOCCRetry

If a conflict due to OCC occurs, the system throws the UpdateConflict exception and it is caught by the Catch statement.

The other new element here is ttsLevel. Since transactions can be nested, we do want the exception to fall through to the parent transaction, if one exists. If ttsabort is issued (directly or due to a throwing error) at any level, the whole transaction will be rolled back; we can’t roll back just to the level where the error is thrown.

The code checks the current TTS level. If it is not zero—in other words, if it is still in a TTS transaction—it throws another UpdateConflict exception to the next Catch list of the next outer Try statement in scope.

This continues until it is no longer inside a TTS transaction. We must make sure that, when the code is retired, all data is refreshed.

It is important that we don’t retry indefinitely, as this may cause the client to hang. To control this, we use xSession::currentRetryCount() to get the number of retries and check this against the #RetryNum macro. The macro defines the standard number of retries deemed appropriate by Microsoft, which is 5.

Then the UpdateConflictNotRecovered exception is thrown. This means the whole transaction is aborted and stops retrying.

 

Click here to download our free ebook on Dynamics 365 and get answers to your questions!

 

Conclusion

We do not, in any case, want an error to be thrown that stops the form from opening. Also, if there is an error, we need to decide whether the user actually needs to know that an error occurred. It may be enough for our purposes that the fields don’t appear, and we can use the debugger to trace through the code to determine why.

Written by The Sherweb Team Collaborators @ Sherweb