In developing a custom AIF service to check out inventory from AX, we determined the service should return an output data contract. The inventory is checked out of stock via the posting of a movement journal. Now, we all know that journal postings of any kind can fail. A failed post throws an exception, of which I was more than happy to handle via a try / catch. However, no matter what kind of catch I had implemented, it wasn't being executed.
When a movement journal doesn't post it is left unposted for the user to try to fix whatever is wrong ( change the financial dimensions, fix the inventory quantity, add a cost to the item, etc. ). When I looked for the unposted journal, it didn't exist. It's as if the entire service call was in a transaction that was rolled back because of the error.
After debugging the call via Visual Studio, it was obvious that all inbound AIF service calls are wrapped in one gigantic transaction. Below is a screenshot from the AifRequestProcessor class, where a ttsbegin statement is left unbalanced:
This is essentially right after the initial handshake between 3rd party application and AX, a transaction is started and left to hang.
And later, after the inbound AIF process is complete, it will close it:
Transaction scope for a try/catch is hugely important. Even if you have a catch block for specific exception types or a generic “catch” for all exceptions, they will not even be used if your code is being executed within an outer transaction. Here is the above example simplified:
To overcome this, a quick hack is to start your service method with a ttscommit. To be sure it is never misused, you can check the transaction level using appl.ttsLevel() to make sure you are in a transaction at the beginning of your custom service.