X++ includes the statements ttsbegin, ttscommit, and ttsabort
for marking the beginning and ending of database transactions. To write
effective business logic for Dynamics AX, you need to understand how
the transaction scope affects exception handling. This section describes
tts-prefixed statements and exception handling, as well as the optimistic and pessimistic concurrency models.
In this section, we include code examples of how the ttsbegin, ttscommit, and ttsabort
statements affect interaction with SQL Server 2005. The X++ statements
executed in the application are written in lowercase letters (select, for example), and SQL statements passed to and executed in the database are written in uppercase letters (SELECT, for example).
1. Transaction Statements
A transaction in X++ starts with ttsbegin and ends with either ttscommit or ttsabort.
When these statements are used to start or end a transaction, the
following equivalent statements are being sent to SQL Server 2005: BEGIN TRANSACTION, COMMIT TRANSACTION, and ROLLBACK TRANSACTION.
In Dynamics AX 2009, transactions behave differently when they begin
and end differently than they did in Dynamics AX 4.0. Whereas Dynamics
AX 4.0 runs on SQL Server 2000 using implicit transaction mode, Dynamics
AX 2009 runs on SQL Server 2005 using explicit transaction mode, so a
transaction in the database is always initiated when a ttsbegin statement is executed. When ttsabort is executed, the equivalent statement ROLLBACK TRANSACTION is executed in the database. The execution of ttscommit results in the execution of COMMIT TRANSACTION if a SQL data manipulation language (DML) statement has been executed after the transaction has started. Otherwise, the ttscommit results in the execution of ROLLBACK TRANSACTION. COMMIT TRANSACTION is executed only if a SELECT, an UPDATE, an INSERT, or a DELETE is executed after BEGIN TRANSACTION. The execution of the different TRANSACTION
statements is illustrated in the following X++ code, in which the
comments show the SQL statements that are sent to the database and
executed.
boolean b = true;
;
ttsbegin; // BEGIN TRANSACTION
update_recordset custTable // First DML statement within transaction
setting creditMax = 0;
if ( b == true )
ttscommit; // COMMIT TRANSACTION
else
ttsabort; // ROLLBACK TRANSACTION
|
You
can, however, have nested levels of transaction blocks to accommodate
encapsulation and allow for the reuse of business logic. Setting up
these accommodations involves the notion of transaction level, also
known as ttslevel, and nested transaction scopes involving inner and outer transaction scopes.
Note
Consider a class developed to update a single customer record within a transaction. This class contains a ttsbegin/ttscommit
block, which states the transaction scope for the update of the single
instance of the customer. This class can be consumed by another class,
which selects multiple customer records and updates them individually by
calling the first class. If the entire update of all the customers is
executed as a single transaction, the consuming class also contains a ttsbegin/ttscommit block, stating the outer transaction scope. |
When X++ code is executed outside a transaction scope, the transaction level is 0. When a ttsbegin statement is executed, the transaction level is increased by one, and when a ttscommit
statement is executed, the transaction level is decreased by one. Only
when the transaction level is decreased from 1 to 0 is the COMMIT TRANSACTION statement sent. The execution of ttsabort causes a ROLLBACK TRANSACTION statement to be sent to the database and the transaction level to be reset to 0.
The following example illustrates the use of nested transactions and TRANSACTION statements sent to the database, as well as the changes in the transaction level.
static void UpdateCustomers(Args _args)
{
CustTable custTable;
;
ttsbegin; // BEGIN TRANSACTION - Transaction level changes from 0 to 1.
while select forupdate custTable
where custTable.CustGroup == '40'
{
ttsbegin; // Transaction level changes from 1 to 2.
custTable.CreditMax = 1000;
custTable.update();
ttscommit; // Transaction level changes from 2 to 1.
}
ttscommit;// COMMIT TRANSACTION - Transaction level changes from 1 to 0.
}
|
Tip
You can always query the current transaction level by calling appl.ttslevel. The returned value is the current transaction level. |
The number of ttsbegin statements must balance the number of ttscommit statements. If the Dynamics AX application runtime discovers that the ttsbegin and ttscommit statements are not balanced, an error dialog box (shown in Figure 1)
is presented to the user, or an error with the following text is
written to the Infolog: “Error executing code: Call to TTSCOMMIT without
first calling TTSBEGIN.”
Note
In
the event of an unbalanced TTS error, you might need to log out of the
Dynamics AX client to reset the transaction level. When you log out, the
started transaction in the database is rolled back. |