In my yesterday’s article about the Preview Posting feature in Microsoft Dynamics NAV 2016 I said there are at least two ways to redesign it to avoid both the unnecessary complication and inherent dangers of the TryFunction feature behavior.
In my first post, I’ll show that it would have been perfectly possible to achieve exactly the same functionality without try functions. And it would take a total of one new line of code, one changed line of code, and six removed lines of code.
Let’s go and simplify it.
Preview Posting depends on several tricks:
- Committing is prevented through the use of CONSISTENT.
- Code that commits is avoided through the PreviewMode flag.
- After relevant entries are created, posting is aborted with a signal error.
- In preview mode, posting always results in an error. TryFunction captures the error from the posting. If error is the signal error, preview collects and presents the posted data, then rolls back. If the error is anything else, it’s rethrown.
That’s a very high-level view over technical jumble going on. Let’s rather take a look at it from intention angle. This is what’s needed:
- Commit must not happen anywhere during preview posting.
- At the end, everything must be rolled back.
Simple. Do we really need to complicate these simple goals with signal errors and try functions and all? No.
“It is vain to do with more what can be done with less.” – William of Occam
Let’s first address the signal error. That’s the clumsiest part of the whole architecture. This is what – in fact – is going on here: NAV throws an error at the end of the posting process, just in case another error didn’t happen earlier, just so it can immediately capture that error, and in case it didn’t capture that error, then it throws whichever other error it captured.
Let me illustrate that using C#:
This makes no sense. It does exactly the same as this:
Apparently, no try..catch block is needed. If there is a (legitimate) error, then let it out, loud and clear. If there is no error, why throwing one just for sake of capturing it? Simply let the execution continue.
So, let’s translate this to C/AL now. There are two points to solve. First is, we need to get rid of the signal error. Go to codeunit 80, and change this:
… into this:
Good, now that the signal error is gone, let’s fix codeunit 81. Simply replace this:
… with this:
And you can now safely clear the TryFunction flag from the Code function as well.
Does this really do the job? Yes. If there is an error inside the Code() call, it immediately shows and rolls back the transaction. If there is no error, then we collect the posted entries, show them, and then roll back the transaction. If there is a commit anywhere inside, it fails due to CONSISTENT(FALSE) that was set inside the Start function.
Obviously, no try..catch approach was needed, as it achieved absolutely nothing. When first analyzing the preview posting feature, I was afraid that the illogical and dangerous behavior of TryFunctions in C/AL was specifically designed to allow preview posting. If that was so – then this behavior is unnecessary, in addition to being wrong. Obviously, the same functionality can be achieved without TryFunctions, with much simpler and easier to follow code.
However, I am still not happy with all this. The whole logic of preview posting calls for rolling back everything that was posted, and Microsoft had to go to extreme lengths with code changes to make sure rollback is actually possible. It first explicitly avoided all commits through setting a flag inside codeunit 80. But this wasn’t enough, because it didn’t take care of any possible future commits that partners may introduce over time. So, to take care of those, there was CONSISTENT(FALSE) to make sure any commit fails.
I said yesterday, and I’ll say it now: that’s a dirty trick. Apparently, what we need here is nested transactions. Why failing at COMMIT, when, if we had nested transactions, inner COMMITs wouldn’t really do anything, let alone anything dangerous. Why not starting an outer transaction, then have as many inner COMMITs as we want, and then simply roll back the outer transaction? Well – you may say – we don’t have inner transactions, that’s why we need to fall back to the CONSISTENT(FALSE) trick.
But we do have inner transactions in C/AL. And that’s the topic for my tomorrow’s post: how to refactor preview posting so that it doesn’t even require CONSISTENT(FALSE) and doesn’t mind if there are any COMMITs anywhere in the code.
What do you think? Should Microsoft simplify the Preview Posting feature by applying the changes I suggested in this blog post?