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!

ListPage not showing the full path in the address bar

Straight to the point 🙄

My list page does not show the full path in the address bar. Please, any suggestion to solve that?

This is quite simple, but I already saw some custom-made ListPages still lacking on this. Basically, when we add a new ListPage to a menu, by default, the address bar can look like this:

contentarea1

The full path of your new form might be missing and this is utterly annoying. But luckily, this ain’t hard to solve. There is a property available in the menu item after you’ve put it on your desired menu called IsDisplayInContentArea, which is disabled at first.

contentarea2

After activating, just close the AX client and open it again. This is the outcome:

contentarea3

Although this is a mere detail, it will surely make all the difference.

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!