As much as I like the transactional support in the core of the X++ language, as much I dislike the connection between exception handling and transactions.
In my opinion transaction should be part of the syntax and have syntax based scope.
Here is my suggestion to Microsoft:
1) ban the standalone ttsbegin and ttscommit
2) replace it with special try-catch syntax , as drafted below:
try(new Connection())
{ //implicit TTSbegin
[do your stuff]
} //implicit TTScommit
catch(Exception::Error)
{ //implicit TTSabort.
[oops]
}
Both Oracle and MSSQL2005 support the nested transactions, and that would be ideal opportunity to use it.
conn = new Connection();
try(conn)
{ //implicit TTSbegin = BEGIN TRANS LEVEL1
try(conn)
{ //implicit TTSbegin = BEGIN TRANS LEVEL2
SalesFormLetterInvoice::post(SalesTable);
} //implicit TTScommit = COMMIT TRANS LEVEL2
catch(Exception::Error)
{ //implicit TTSabort = ROLLBACK TRANS LEVEL2
if(..cannot compensate the error...)
throw Error(....)
else
[compensate the error.]
}
} //implicit TTScommit = COMMIT TRANS LEVEL1
catch(Exception::Error)
{ //implicit TTSabort = ROLLBACK TRANS LEVEL1
[...]
}
That way each try-catch block is eligible to work, not only this started outside of transaction, and when needed each of them can operate own (sub) transaction.
It is also much less demanding on the developer, preventing improper or unbracketed use of ttsbegin and ttscommit. Normal (non db) try-catch bloack work as usually, allowing to catch X++ and CLR exceptions without unwanted rollbacks.
3 comments:
I think it would be better and more general to borrow the 'using'keyword from C#
using(new Transaction())
{
select ...
}
That's even better, but I affraid 'using' clause does not distinguish the reason why object goes out of scope, which is important here. If execution arrives to end of scope implicit ttscommit should happen.
It need to act like:
using(new Transaction())
{ //implicit TTSbegin = BEGIN TRANS LEVEL1
select
[...]
if(....)
throw Error() //implicit TTSAbort = ROLLBACK TRANS LEVEL1
} //implicit TTSCOMMIT
maybe, it should extend the 'using' semantic.
using (a)
{
xxx
}
==>
try
{
xxx
a.Dispose();
}
catch
{
a.ExceptionDispose();
}
Post a Comment