Performance issue running a query/view in Dynamics AX

Every time you need to design a query that devours a lot of data, you need to be careful enough so it doesn’t take forever to process.

Once in a project, I was asked to build a view with more than 10 DataSources, so I’ve decided to create a view complying to every AX best practice I could intending to improve performance. However, that didn’t save me from performance issues. The view took around 10 minutes to complete running directly from AOT.

After reviewing all branches and relations of my query, I didn’t find anything wrong so I decided to take a look directly at the SQL and use the Display Estimated Execution Plan function, which helped me realize missing indexes that could improve my view performance by almost 70%.

The following images demonstrates how the missing indexes were identified:

  1. Create a SQL select statement to get all records from your view.
  2. Run the Display Estimated Execution Plan:

    SQLFindMissingIdx

  3. Right-click on the missing index and choose the option Missing index details:

    missingIdxDetails

  4. It will open a new SQL statement with the missing index script:

    SQL IDX

  5. Run the script and create the missing index.

Well done! After that you can go back to AX and check the performance improvement with the new Index.

In my case, the view started to show all records in about 2 minutes instead of 10!

I highly recommend you to create the previous index inside the AX, to keep it organized and so you can easily have it in the other SQL databases that you might deploy your view.

See you guys next time!

AX Support for pessimistic locking

This was probably a subject of discussion in various other blogs and forums. But I’ve found a very interesting approach to this matter by Martin Dráb long time ago.

Most developers already know the basics about transaction usage and how the mechanics behave while locking a record for an update instruction. But once in a while we can face different situations.

Cannot edit a record in MyTable.
Cannot call NEXT, update(), or delete() on buffer where data is selected or inserted in another transaction scope.
Calls to NEXT, update(), or delete() must be performed on the buffer on the selection transaction level, or within the same transaction (TTS) scope.

This can actually happen in a fairly simple composition:

while select forUpdate myTable
{
    ttsBegin;
    myTable.MyField = 'something';
    myTable.update();
    ttsCommit;
}

We can come across such a code really often and it works very well under most circumstances. Specifically, it requires optimistic record locking to be active. In such cases, Dynamics AX just check whether a record is selected for update and that the update() method is called inside a transaction – that’s fulfilled and everything works.

But if the table uses pessimistic locking, the update fails with the run-time error we’ve shown.

That happens because pessimistic locking needs to place a lock on the selected record and it must happen in the same transaction, which is obviously not complied here.

Optimistic locking is available since Dynamics AX version 4 and it’s the default method of record locking. However, It’s the default, but not the only one – pessimistic locking can be activated in following ways:

  1. For a single query (select pessimisticLock myTable or myTable.concurrencyModel();
  2. For a single table (property OccEnable = No);
  3. Globally for the whole AX (Administration > Setup > System > Concurrency model configuration (AX4, AX2009), or System administration > Setup > Database > Select concurrency mode (AX2012)).

In that moment the code mentioned above fails. In other words, the code works only in a specific configuration of Dynamics AX and ends with a run-time error otherwise.

The question is – is it necessary to write database queries to work also with another configuration of locking?

  • Statement no. 1: Configuration of locking is the standard part of Dynamics AX and we shouldn’t arbitrarily limit the existing functionality. Pessimistic locks can help to resolve excessive number of write conflicts in some parts of application etc.
  • Statement no. 2: A change of locking method in an existing application is utterly exceptional and it’s not worth to support it, because it can have unnecessary performance implications.

In my case, I could either select every record for an update individually:

while select forUpdate myTable
{
    ttsBegin;
    myTable.reread(); //reread record in the same buffer
    myTable.MyField = 'something';
    myTable.update();
    ttsCommit;
}

or to change the transaction logic to consider the whole cycle as one atomic operation:

ttsBegin;
while select forUpdate myTable
{
    myTable.MyField = 'something';
    myTable.update();
}
ttsCommit;

The first approach significantly increases the number of database queries, the second one requires the change of transaction logic and it potentially locks a large amount of records.

In my case, myTable.reread() was enough to solve the problem. However, it’s reasonable to always analyze the big picture and decide if OCCEnabled feature should be used or not, or what alternative might be used for the greatest result.

We should never ignore pessimistic locking. I hope that this example was useful in order to help demonstrate and get you the insight about all scenarios we can stumble.

See you next time! 🙂

Top 10 issues discovered from Dynamics AX Code Review

It’s not hard to find yourself stumbled in a lot of common development mistakes. During code review, we usually see a lacking of best practices and an overall misconception towards the x++ development.

This interesting article from Bertrand Caillet (MSDN) gives us some great examples and possible solutions for the most frequent ones.

http://blogs.msdn.com/b/axinthefield/archive/2014/02/18/top-10-issues-discovered-in-the-dynamics-ax-code-review.aspx

It’s always good to read articles like this and spread the word to your co-workers or development team. It’s certainly a good way to avoid many of the same errors in future implementations.

See you guys next time!

How to change the AX environment background color

We often observe some people struggling to know in which environment they are. This simple idea can help you out, it’s an example on how to customize the background color for all AX formularies.

To accomplish that, we can overwrite the init method from the SysSetupFormRun class:

public void init()
{
    super();

    if (this.isWorkflowEnabled())
    {
        workflowControls = SysWorkflowFormControls::construct(this);
        workflowControls.initControls();
    } 

    this.design().colorScheme(FormColorScheme::RGB);
    this.design().backgroundColor(WinAPI::RGB2int(152,251,152));
}

Once you have done this, all AX forms should look like this:

form in green

As you can see from the method, we’re using the RGB color model. For a better understanding in how it works, please check the following link:

http://www.rapidtables.com/web/color/RGB_Color.htm

I also recommend you to create a new field on the CompanyInfo table, which will allow users to choose the color they want. In this scenario, you need to add some code inside the lookup method so the system can use the RGB Window while clicking the color field.

public void lookup()
{
    container   c;
    int         colorNum;

    c = WinAPI::chooseColor(this.hWnd(),0,0,0,NULL);

    if (conlen(c))
    {
        colorNum = WinAPI::RGB2int(conpeek(c,1),conpeek(c,2),conpeek(c,3));

        CompanyColorCtrl.colorScheme(FormColorScheme::RGB);
        CompanyColorCtrl.backgroundColor(real2int(colorNum));
        CompanyColorCtrl.backgroundColor(real2int(colorNum));
    }
}

When you have different environments such as development, testing and production it can be very helpful.

See you guys next time!

Try Catch and transactions

Exception handling can be tricky sometimes, so this post can help you figure out the behavior of the try/catch and transactions.

The general rule is that exceptions are caught in the outer most catch, where the ttslevel is 0. This means that if you put a transaction around a try/catch, your exceptions will not be caught.

The following jobs demonstrate that:

    try
    {
        ttsBegin;
        throw error("an error");
        ttsCommit;
    }
    catch
    {   
        info("error caught");
    }

The output will be:

tryCatch1

Try/catch inside transaction:

    ttsBegin;
    try
    {
        throw error("an error");  
    }
    catch
    {   
        info("error caught");
    }
    ttsCommit;

Output:

tryCatch2

As you can see, the error is not caught when the transaction is around the try catch. However, there are two exceptions to this rule. The following code demonstrates that UpdateConflict and DupplicateKeyException can be caught inside a transaction.

    Set             set = new Set(Types::Enum); // a set containing all possible exceptions
    SetEnumerator   se;                         // enumerator used to loop though the set with exception
    Exception       exception;                  // used to cast the value from the set
    boolean         caughtInside;
    ;
    
    // add all exception to a set
    set.add(Exception::Break);
    set.add(Exception::CLRError);
    set.add(Exception::CodeAccessSecurity);
    set.add(Exception::DDEerror);
    set.add(Exception::Deadlock);
    set.add(Exception::DuplicateKeyException);
    set.add(Exception::DuplicateKeyExceptionNotRecovered);
    set.add(Exception::Error);
    set.add(Exception::Info);
    set.add(Exception::Internal);
    set.add(Exception::Numeric);
    set.add(Exception::PassClrObjectAcrossTiers);
    set.add(Exception::Sequence);
    set.add(Exception::Timeout);
    set.add(Exception::UpdateConflict);
    set.add(Exception::UpdateConflictNotRecovered);
    set.add(Exception::Warning);
    
    // create enumerator
    se = set.getEnumerator();
    
    // loop all exceptions
    while(se.moveNext())
    {
        // set flag false
        caughtInside = false;
        // begin outer try catch
        try
        {
            ttsBegin;
            // begin inner try catch
            try
            {
                // cast exception
                exception = se.current();
                // trhow exception
                throw exception;
            }
            catch
            {
                // set flag to indicate the exception was caught inside the transaction
                caughtInside = true;
                
                warning(strFmt("%1 can be caught inside transaction", exception));
                // throw exception again to catch it outside of transaction
                throw exception;
            }
            ttsCommit;
        }
        catch
        {
            // for once, it's ok to catch everyting 🙂   
            
            if(caughtInside)
            {
                warning(strFmt("%1 can alse be caught outside of the transaction", exception));
            }
            else
            {
                info(strFmt("%1 can only be caught outside of the transaction", exception));
            }
            
        }
    }

Output:

tryCatch3

It is also noteworthy that, when you enter a catch block, there has been an implicit ttsabort, so the transaction has been rolled back. However, this is not true for UpdateConflict and DuplicateKeyException when they were caught inside a transaction.

I hope this was helpful for you guys. It was actually inspired by a post I’ve read a long time ago at the Art Of Creation blog. You should definitely check it out, one of the greatest out there http://www.artofcreation.be/ 🙂

Customizing the context menu

This is a very brief and simple post, the title itself explains what we’re about to show you. This is an example on how to customize the context menu for any form controls that supports context menus.

Let’s say you want to completely change the available options or add some more. You can either go with context or showContextMenu(), they should work very similarly.

We’ve created a form with only one string edit control. By overriding the context method, we had this outcome:

contextmenu1

And by clicking the custom-made context menu:

contextmenu2

You can place menu items to call different things. It’s up to you!

public void context()
{
    PopupMenu m = new PopupMenu(element.hWnd());

    // Save the handle of the appended menu item.
    int myMenuItem = m.insertItem("My Item");
    int selectedItem;

    // Display the menu.
    selectedItem = m.draw();

    // Check if the user selected myMenuItem.
    switch (selectedItem)
    {
        case myMenuItem:
            info("My Item was selected");
            break;
    }
}

See you next time 🙂

Export all project definitions

This post is similar to the last one. But instead of exporting the whole thing, this will only export the project definitions.

But why even bother doing this? The answer is that you can face some sort of situation in that you are not 100% sure about the difference between two or more enviroments. Let’s say you don’t want to make any modification in the objects yet, exactly, but you want to import the project definitions so you can check them in a more friendly and easier way.

It can be useful if you want to organize different development environments that you’ve lost track somehow.

static void EDC_exportProjectDefinitions(Args _args)
{
    TreeNode        node;
    ProjectNode     project;
    str             path, folder = "C:\\AX\\Projects\\";

    project = SysTreeNode::getSharedProject().AOTfirstChild();

    while(project)
    {
        node = project.loadForInspection();

        path = strfmt("%1%2_%3.xpo"
		    , folder
		    , enum2str(node.applObjectType())
		    , node.treeNodeName());

        node.treeNodeExport(path, 2049);

        project = project.AOTnextSibling();
    }
    info("Complete");
}