When working with extensions in Dynamics 365 Business Central, you’ve always to keep in mind that you’re now in a complex ecosystem where you’re not alone and someone can breakes things.
One of the sneaky problem you can have with different extensions on a SaaS tenant is related to handling commits of transactions. Let’s imagine to have an extension called MAIN_APP that has a business process defined as follows:
Here the business process is composed of 3 steps (ExecuteProcess1, ExecuteProcess2 and ExecuteFinalProcess procedures). Before calling the ExecuteFinalProcess procedure, we raise an integration event for our subscribers (third-party extension’s integration) and ONLY if the ExecuteFinalProcess procedure returns TRUE, the transaction is committed.
Now imagine that a third-party extension subscribes to the OnBeforeFinalProcess event and does something like the following:
The third-party extension’s event subscriber here executes a custom process (ThirdPartyProcess procedure) and then performs a Commit() operation. In this way, it commits the transaction before your MAIN_APP process is committed and this can result in a wrong process result.
To avoid this, starting from Dynamics 365 Business Central version 17 you can now specify the behavior of a commit operation inside a method scope (procedure) or inside an event by using the CommitBehavior attribute. Possible values are:
- CommitBehavior::Ignore: all commit calls are ignored until the method scope ends
- CommitBehavior::Error: it throwns an error if a commit operation is executed before the end of the scope of the method.
In the above example, for having a safe transaction we can define our Integration event in the MAIN_APP extension as follows:
By defining CommitBehavior::Ignore in the event definition, any subcribers attempt to commit the transaction will be ignored and the transaction will be committed only at the end of our process.
Please also remember that if the parent method is executed with a more restrictive behaviour (for example CommitBehavior::Error) then the current method will continue to respect the more restrictive behaviour defined.
Small things but important to have safe transactions in the extension’s universe…