Book Image

Microsoft Dynamics AX 2009 Development Cookbook

By : Mindaugas Pocius
Book Image

Microsoft Dynamics AX 2009 Development Cookbook

By: Mindaugas Pocius

Overview of this book

Microsoft Dynamics AX provides a comprehensive Enterprise Resource Planning (ERP) solution for mid-size and larger organizations. Dynamics AX implementations are used worldwide by thousands of customers. With the new version - Dynamics AX 2009 - the system is due to expand even more rapidly. Every new implementation requires some level of customization, and all organizations want this to be done to the highest standards using proven approaches. This step-by-step guide will help you manage your company's ERP information and operations efficiently, and solve your business process problems in an effective and quick way. This book focuses on commonly used custom modifications in major Dynamics AX modules. This book contains commonly used bits of code which were real-world tested in at least one successful Dynamics AX implementation. Many of the recipes were deployed in many implementations and even across several versions of Dynamics AX. The examples in this book cover various areas of Dynamics AX to help developers not only learn about programming, but also about the functional side of Dynamics AX. The book should help developers who already know the basics of Dynamics AX programming to step up to the next level. The recipes allow you to look at the development from the perspective of business processes. You will learn to enhance your user interface using various Dynamics AX UI elements. Managing your data and functions will become easier. You will also get numerous development tips and tricks from a Dynamics AX development expert.
Table of Contents (12 chapters)
Microsoft Dynamics AX 2009 Development Cookbook
Credits
About the Author
About the Reviewers
Preface
Index

Executing a direct SQL statement


Dynamics AX allows developers to build X++ SQL statements flexible enough to fit any custom business process. But in several cases, the usage of X++ SQL is either not effective or not possible at all.

One of the cases is when we run data upgrade tasks during an application version upgrade. The standard application contains a set of data upgrade tasks to be completed during the version upgrade. If the application is highly customized, then most likely, the standard tasks has to be modified to reflect data dictionary customizations or even a new set of tasks have to be created to make sure data is handled correctly during the upgrade.

Normally, at this stage, SQL statements are so complex that they can only be created using database-specific SQL and executed directly in the database. Additionally, running direct SQL statements dramatically increases data upgrade performance because most of the code is executed on the database server where all data resides. This is very important while working with large volumes of data.

Another case when we would need to use direct SQL statements is when we want to connect to an external database using the ODBC driver. In this case, X++ SQL is not supported at all.

This recipe will demonstrate how to execute direct SQL statements. We will connect to the current Dynamics AX database directly using an additional connection and will retrieve the list of vendor accounts.

How to do it...

  1. Open AOT, and create a new class called vendTableSql with the following code:

    class VendTableSql
     {
     }
     server static void main(Args _args)
     {
         UserConnection                  userConnection;
         Statement                       statement;
         str                             sqlStatement;
         SqlSystem                       sqlSystem;
         SqlStatementExecutePermission   sqlPermission;
         ResultSet                       resultSet;
         DictTable                       tblVendTable;
         DictField                       fldAccountNum;
         DictField                       fldName;
         DictField                       fldDataAreaId;
         DictField                       fldBlocked;
         ;
     
         tblVendTable = new DictTable(tablenum(VendTable));
     
         fldAccountNum = new DictField(
             tablenum(VendTable),
             fieldnum(VendTable,AccountNum));
     
         fldName = new DictField(
             tablenum(VendTable),
             fieldnum(VendTable,Name));
     
         fldDataAreaId = new DictField(
             tablenum(VendTable),
             fieldnum(VendTable,DataAreaId));
     
         fldBlocked = new DictField(
             tablenum(VendTable),
             fieldnum(VendTable,Blocked));
     
         sqlSystem = new SqlSystem();
     
         sqlStatement = 'SELECT %2, %3 FROM %1 ' +
             'WHERE %4 = %6 AND %5 = %7';
     
         sqlStatement = strfmt(
             sqlStatement,
             tblVendTable.name(DbBackend::Sql),
             fldAccountNum.name(DbBackend::Sql),
             fldName.name(DbBackend::Sql),
             fldDataAreaId.name(DbBackend::Sql),
             fldBlocked.name(DbBackend::Sql),
             sqlSystem.sqlLiteral(curext(), true),
             sqlSystem.sqlLiteral(CustVendorBlocked::No, true));
     
         userConnection = new UserConnection();
         statement      = userConnection.createStatement();
     
         sqlPermission = new SqlStatementExecutePermission(
             sqlStatement);
     
         sqlPermission.assert();
     
         resultSet      = statement.executeQuery(sqlStatement);
     
         CodeAccessPermission::revertAssert();
     
         while (resultSet.next())
         {
             info(strfmt(
                 "%1 - %2",
                 resultSet.getString(1),
                 resultSet.getString(2)));
         }
     }
  2. Run the class, and notice the list of vendors retrieved directly from the database:

How it works...

We start the code by creating DictTable and DictField objects for handling the vendor table and its fields later in the query.

A new SqlSystem object also has to be created. It will be used to convert Dynamics AX types to SQL types.

Next, we set up a SQL statement with %1, %2, and other placeholders for table or field names or values to be inserted later.

The main query creation happens next when the query placeholders are replaced with the right values. Here we use previously created DictTable and DictField type objects by calling their name() methods with the DbBackend::Sql enumeration as an argument. This ensures that we pass the name exactly how it is used in the database. For example, the Dimension field in VendTable actually consists of a set of three fields, which are named in the database DIMENSION, DIMENSION2_, and DIMENSION3_ respectively and using DbBackend::Sql would return us exactly that.

We also use the sqlLiteral() of the previously created sqlSystem object to properly format SQL values to make sure that they do not have any unsafe characters.

Once the SQL statement is ready, we initialize a direct connection to the database and run the statement. The result is a resultSet object, and we get the results by using the while statement and calling its next() until the end.

Note that we create an sqlPermission object of type SqlStatementExecutePermission here and call its assert() before executing the statement. This is required in order to comply with Dynamics AX SQL execution security requirements.

Another thing to mention is that when building direct SQL queries, special attention has to be paid to license, configuration, and security keys. Some tables or fields might be disabled in the application and may not be present in the database.

The code in this recipe can be also used to connect to the external ODBC databases. We only need to replace the UserConnection class with OdbcConnection and use text names instead of DictTable and DictField.

There's more...

The Standard Dynamics AX application provides an alternate way of building direct SQL statements by using a set of SQLBuilder classes. By using those classes, we can create SQL statements as objects as oppose to text. We will demonstrate next how to use a set of SQLBuilder classes. We will create the same SQL statement as before.

First in AOT, we create another class called VendTableSqlBuilder with the following code::

class VendTableSqlBuilder
 {
 }
 server static void main(Args _args)
 {
     UserConnection                  userConnection;
     Statement                       statement;
     str                             sqlStatement;
     SqlStatementExecutePermission   sqlPermission;
     ResultSet                       resultSet;
     SQLBuilderSelectExpression      selectExpr;
     SQLBuilderTableEntry            vendTable;
     SQLBuilderFieldEntry            accountNum;
     SQLBuilderFieldEntry            name;
     SQLBuilderFieldEntry            dataAreaId;
     SQLBuilderFieldEntry            blocked;
     ;
 
     selectExpr = SQLBuilderSelectExpression::construct();
 
     vendTable = selectExpr.addTableId(
         tablenum(VendTable));
 
     accountNum = vendTable.addFieldId(
         fieldnum(VendTable,AccountNum));
 
     name = vendTable.addFieldId(
         fieldnum(VendTable,Name));
 
     dataAreaId = vendTable.addFieldId(
         fieldnum(VendTable,DataAreaId));
 
     blocked = vendTable.addFieldId(
         fieldnum(VendTable,Blocked));
 
     vendTable.addRange(dataAreaId, curext());
     vendTable.addRange(blocked, CustVendorBlocked::No);
 
     selectExpr.addSelectFieldEntry(
         SQLBuilderSelectFieldEntry::newExpression(
             accountNum,
             'AccountNum'));
 
     selectExpr.addSelectFieldEntry(
         SQLBuilderSelectFieldEntry::newExpression(
             name,
             'Name'));
 
     sqlStatement   = selectExpr.getExpression(null);
 
     userConnection = new UserConnection();
     statement      = userConnection.createStatement();
 
     sqlPermission = new SqlStatementExecutePermission(
         sqlStatement);
 
     sqlPermission.assert();
 
     resultSet = statement.executeQuery(sqlStatement);
 
     CodeAccessPermission::revertAssert();
 
     while (resultSet.next())
     {
         info(strfmt(
             "%1 - %2",
             resultSet.getString(1),
             resultSet.getString(2)));
     }
 }

In this method, we first create a new selectExpr object, which is based on the SQLBuilderSelectExpression class. It represents the object of the SQL statement.

Next, we add the VendTable table to it by calling its member method addTableId(). The method returns a reference to the vendTable object of type SQLBuilderTableEntry, which corresponds to a table node in a SQL query.

Then, we create four field objects of type SQLBuilderFieldEntry to be used later and two ranges to show only this company account and only active vendor accounts.

We use addSelectFieldEntry() to add two fields to be selected. Here we use the previously created field objects.

The SQL statement is generated once getExpression() is called and the rest of the code is the same as in the previous example.

Running the class would give us results exactly like the ones we got before.