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?
This Post Has 12 Comments
Considering how much flak the Try function is taking, eliminating it entirely should perhaps be MS main consideration?
I don’t think so. TryFunction is a great concept, just implemented the wrong way. If a database error happens inside, and transaction was in progress, it should mark the transaction as uncommittable, and that’s all. Also, if it encounters a COMMIT while executing, it should ignore it. That would solve all problems we currently have with it, plain and simple.
Could you please share this solution because I’m having same problem and I just need the commit to be ignored.
Well, “this solution” is right here in the post. I don’t know what else I could share. Plus, it was seven years ago, if I had any code sitting somewhere back then, I certainly don’t have it anymore. Sorry 🙁
They should consult you BEFORE they implement Preview postings and TryFunctions 😉
I tend to say a very big YES! MS, Simply and apply code as shown here 🙂
Then again, are they really that dumb at MS that they didn’t think about it themselves and build TryFunctions deep in the runtime?
Can’t really wrap my head around it…..
MS is not dumb, NAV dev/support group that is. I wont speak for the sale, management, brass etc. 🙂
The base NAV code is not always written consistently, does’nt adhere to Impuls Check rules, design patterns nor is it commented.
It has to be done by alot of different people, i pressume.
Well said. However, the Impuls Check tool wouldn’t reveal this problem. This is simply a misdesigned or overly designed feature. I would blame it on eagerness to release a major big thing every year, with a big fat list of functional and platform updates/improvements. NAV 2016 would have been a better platform without TryFunction the way it was released – and it would leave more time to polish TryFunction for a release at a later day.
The Posting Preview isn’t really new stuff, the French version has had this functionality for years.
Apparently the Russian version, too. I wasn’t aware of that – but it was suspicious to me that we got the same in W1 together with TryFunction, and I thought it might have to do with the TryFunction.
If I try to merge our add-on for Financial Management, which naturally brings a lot of changes in Codeunit 12, into the French NAV 2015 version, I get a huge conflict in that old ‘simulation of entries’ area. With the new approach in NAV 2016, these conflicts do not exist anymore. So it does have some advantages from a source code merging process viewpoint, but currently it appears to be a two-edged sword. Thanks for your analysis.
Well, at least we now know how to fix the double-edged sword problem. It’s fairly easy to do with applying the concepts I explained in this article. It remains at the same functional level, without the transaction integrity risk due to TryFunctions.