Did I CATCH your attention yet? At least I did TRY.
Now that I have completed my series on exception handling in C/AL, a very valid question pops up: why don’t we have try..catch syntactical constructs in C/AL, the way we have it in other programming languages?
If there was a top list of C/AL features that people could vote, no doubt this would win without much competition. Wouldn’t it just be an insanely useful C/AL feature if you could write code such as this:
Or something along these lines… Yes, it would be just beautiful beyond comprehension.
And now let me ask you a question (I know the answer already, I just want you to ask yourself this question): Do you know when we are going to have this feature in C/AL?
And now let me make a heretical statement: it’s a good thing that it will never be a part of C/AL.
(Well, of course, I’d love it if I were ever proven wrong by Microsoft, oh I’d just love it so much. But unfortunately, I am pretty darned sure I am right on this one. And let me explain why I believe so.)
It all has to do with database and how C/AL handles write transactions. In C/AL you have no means of controlling transactions – they are always implicitly started when the first database write statement is executed, and it automatically commits when the code execution completes, or when you explicitly call the COMMIT function. However, the problem is – there is no ROLLBACK function in C/AL – the transaction is rolled back implicitly whenever there is any kind of error.
Take a look at this piece of code:
We start by creating a new customer, and then we call a codeunit. If there is an error inside this codeunit , the whole transaction is rolled back (including this customer we just created). So far, so good, and simple.
However, what will happen if you do this:
Exactly. The mother of all ugly errors will happen:
“The following C/AL functions are limited during write transactions because one or more tables will be locked… Codeunit.Run is allowed in write transactions only if the return value is not used.”
Now, why is that? Why exactly does this error happen? What exactly is so different here, than it was with the first code example?
This is what happens: when the first line of code executes, a transaction is implicitly started. Now, the IF CODEUNIT.RUN construct says that if the codeunit execution fails, your code continues executing. However, what happens to the database changes? Well, we already know, when an error happens, the database changes are rolled back. The problem is, which database changes? Only those that happened inside the codeunit, or also any changes that happened before you entered the codeunit? To avoid guessing, NAV fails with this wordy error message and asks you to restructure the code.
Now imagine we had the TRY..CATCH block in C/AL. What if there are five successful database write operations in the TRY block, and then an error is encountered? Should these five operations be committed, or should they be rolled back?
You see, try..catch blocks are easy if we have no transactions. But once transactions enter the playground, suddenly you have a valid question about what should happen uncommitted changes if an error is successfully trapped.
Of course there is a solution to that: being able to control the transactions directly. Just like in T-SQL. T-SQL has TRY..CATCH blocks, but they don’t handle anything directly, they still require you to manage the transactions. In fact, if an error happens in the TRY..CATCH block in T-SQL, the transaction is not automatically rolled back, but it is uncommittable. You could explicitly make it committable through the use of savepoints, and then explicitly rollback to the last successful savepoint. However, to successfully use this T-SQL feature, C/AL would have to add a savepoint after every single line, and then keep track of which line executed successfully to know to which savepoint it should roll back.
Obviously, the only possible way to enable TRY..CATCH in C/AL would be to introduce explicit transactions to it. And doing that would break the whole foundation of C/AL and the fact that it is so simple, precisely because – among other things – it handles transactions completely transparently. Explicitly handling transactions would open a huge door for classes of errors unimaginable today, and it would turn programming in C/AL into a real nightmare.
And that’s why, ladies and gentlemen, there won’t be such thing as a TRY..CATCH block in C/AL. Ever. And it’s a good thing.