11.09.2007, 05:42 | #1 |
Участник
|
Issues concerning X++: Exception handling and transactions
Источник: http://blogs.msdn.com/x/archive/2007...nsactions.aspx
============== The runtime semantics of exceptions is peculiar in X++. The difference from the semantics from other languages is that exception handling considers any catch blocks that are in a scope that contains a running transaction as ineligible to handle the exception. Consequently the behavior of exceptions is quite different in the situation where a transaction is running and in the situation where no transactions are running. This makes the exception semantics different from exception semantics of other languages, even though the syntax is very much the same. This is a perfect setup for confusion even for seasoned X++ programmers. Please consider the example below for the following discussion: X++: 1: try 2: { 3: MyTableType t; 4: ttsBegin; // Start a transaction 5: try 6: { 7: throw error(Something bad ); 8: } 9: catch 10: { 11: // Innermost catch block 12: } 13: update_recordset t // Must run within a transaction 14: ttsCommit; // Commit the transaction 15: } 16: catch 17: { 18: Info (In outermost exception ); 19: } 20: 20: This behavior is by design. Whenever an exception is thrown all transactions are rolled back (except for two special exceptions namely Exception::UpdateConflict and Exception::UpdateConflictNotRecovered, that do not roll back the active transactions). This is an inseparable part of the semantics of throwing exceptions in X++. If the exception is not one of the two exceptions mentioned above, the flow of control will be transferred to the handler of the outermost block that contains a TTSBegin. The reasoning behind this is that the state of the database queries would not be consistent when the transactions are rolled back, so all code potentially containing this code is skipped. Another issue is balancing of the transaction-level. Please consider an amended example: X++: 1: try 2: { 3: MyTableType t; 4: ttsBegin; // Start a transaction – Increment TTS-level to 1 5: try 6: { 7: ttsBegin; // Start a nested transaction – Increment TTS-level to 2 8: throw new SysException(Something bad ); 9: ttsCommit; // Decrease TTS-level to 1 10: } 11: catch 12: { 13: // Innermost catch block 14: // What is the tts level here? 15: info (appl.ttslevel()); 16: // Something bad happened -> abort the transaction 17: ttsAbort; 18: } 19: update_recordset t // Must run within a transaction 20: ttsCommit; // Decrease TTS-level to 0, and commit 21: } 22: catch 23: { 24: Info (In outermost exception ); 25: } TTSlevel is 1: This won’t work, as the innermost catch block must be able to compensate (and commit) or abort the level-2 transaction. TTS Level 1 would mandate that the system has taken the decision to either abort or commit the level-2 transaction. TTSlevel is 2: This won’t work either, as the innermost catch block should be able to abort the level-2 transaction. A ttsabort inside the innermost catch block will also abort the level-1 transaction, and render the code following the catch block void. This in turn requires that the innermost catch block throws a new exception, and then we end up in the outermost catch block anyway. I hope this provides a little clarity in these murky waters. Источник: http://blogs.msdn.com/x/archive/2007...nsactions.aspx
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору. |
|
|
За это сообщение автора поблагодарили: alex55 (1). |
14.02.2009, 00:46 | #2 |
MCTS
|
Цитата:
This behavior is by design. Whenever an exception is thrown all transactions are rolled back (except for two special exceptions namely Exception::UpdateConflict and Exception::UpdateConflictNotRecovered, that do not roll back the active transactions).
Info Сообщение (00:41:37) rec count: 3 Info Сообщение (00:41:37) level 1 catched Info Сообщение (00:41:37) level 1 completed Info Сообщение (00:41:37) rec count: 0 X++: static void Test_TTS_1(Args _args) { Test_TTS_1 Test_TTS_1; ; delete_from Test_TTS_1; try { ttsbegin; //tts level 1 try { ttsbegin; //tts level 2 try { Test_TTS_1.insert(); Test_TTS_1.insert(); Test_TTS_1.insert(); select count(recid) from Test_TTS_1; info("rec count: " + int2str(Test_TTS_1.RecId)); //throw error("my exception"); throw Exception::UpdateConflictNotRecovered; //throw Exception::UpdateConflict; } catch //implicit ttsabort { info("level 3 catched"); } ttscommit; info("level 3 completed"); } catch //implicit ttsabort { info("level 2 catched"); } ttscommit; info("level 2 completed"); } catch //implicit ttsabort { info("level 1 catched"); } info("level 1 completed"); select count(recid) from Test_TTS_1; info("rec count: " + int2str(Test_TTS_1.RecId)); } |
|
|
|