2018 m. gegužės 3 d., ketvirtadienis

Dynamics AX Logical Expression Optimization

Hello AX World,

Recently I was reading about Chain of Command (CoC) and found one important statement that I was aware of but have never found in any of the documentation.

"Because logical expressions are optimized, calls to next can't occur in logical expressions. At runtime, the execution of the complete expression isn't guaranteed."
From <https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/extensibility/method-wrapping-coc>

The important part is about that logical expressions are optimized in Dynamics AX. Here I will try to explain how it is optimized and why it is important to know it.

It is important to know that as it may help you:
  1. to avoid getting in situations where your code is not executed and it should be.
  2. to improve performance by not necessarily executing code that is time consuming;

Have a look at this and consider if all expressions are tested?

The answer is it depends on expression. 
My experience shows that it starts testing operands from left to right.

Have a look at this code sample.

Code sample 1

Running this would result in  expression1 tested only. It returns true for the first operand so for optimization purposes it skips all the others.

Code sample 2

Lets change so expression1 returns false.

Now it checked expression1 and expression2 and skipped the 3rd.

If I make expression2 return false too it will force to test expression3.

Lets have another example with different operators in expression.

Code sample 3

The result is expression1 and expression3 tested and expression2 is skipped for optimization.

Or have a look at the next example.

Code sample 4
I have changed expression1 and expression2 to return false.

The result is expression3 skipped as the first AND operand returned false, so there is no meaning to test expression3.

You can play around with similar code but the message is quite clear: be aware of this optimization.
It applies to all versions of Dynamics AX. 

There are many examples of this in standard application and I used to develop having this optimization in mind as I like fewer code for better readability.

Sometimes it might skip some code that you would not like to skip or it might help to improve performance. Though there are more clear ways to improve performance.

Few examples from standard code...



Be aware and take care!

2016 m. sausio 7 d., ketvirtadienis

AX7 Control List Should be Sorted Differently

Hello AX World,

AX7 will soon be released and I started training on it. Though don't have access to it yet.
However, I have noticed one thing that might get some developers annoyed when building forms.

Have a look at this long list of controls (see the image below).
Let's say you need to add a Tab control which is very common to many forms. You have to scroll down every time you need that one, or String, or Static Text... It is not handy, right?

I don't know if there is an option to change the order of these controls but I would prefer having Top 10 controls (or Recent list, that would update based on a developer) on top and the rest alphabetically ordered than just this alphabetical list.

Top 10 controls should include Action pane, Button, Button Group, Group, Grid, Menu Item Button and Tab. That's just my opinion.
Be aware and take care!

2015 m. birželio 17 d., trečiadienis

AX Source Code Quality: Top 10 X++ Coding Commandments 1.1

Hello AX Word,

This time I came up with an idea to list top 10 coding guidelines. We would all write a better code if we followed them.

      0. You shall check your code for best practices.
  1. You shall use a proper case for X++ code: start variables, functions, keywords with a lower letter; start application elements with a capital letter.
  2. You shall indent you code appropriately.
  3. You shall add curly brackets around every if, else, loop, switch (except case) and while select statement.
  4. You shall use meaningful names of variables, functions and application elements.
  5. You shall add a single spaces around operators, after commas, if, switch, case, loop keywords.
  6. You shall not use spaces before function parentheses, case's semi-column, ++ and -- operator, after ! (exclamation mark).
  7. You shall avoid using hard-coded values, especially for interface texts.
  8. You shall breakdown long statements.
  9. You shall use column style alignment.
  10. You shall cleanup unused code.
The list is subject to change if I get a more valuable best practice than one above. So this was named 1.0 :)

P.S. I wonder if there is beautifier tool somewhere in the market?

Be aware and take care!

2015 m. birželio 8 d., pirmadienis

AX Source Code Quality: Complex Select Statement Layout

Hello AX World,

One of the key factors that make your code look ugly is layout. There is a very good guidance for that on MSDN site. Everything looks clear when you read it. But when it comes to coding you start wondering how should I make this select statement adhere to best practices? How should I align this complex where clause?

My basic rules are:

  1. Follow MSDN guidelines for select statement.
  2. Use proper indentation.
  3. Keep field list compact.
  4. Make where clause look nice.

Follow MSDN guidelines for select statement.

It also includes while select, update_recordset and delete_recordset.

There are some rules which I am not big fan of.
First is that boolean operators should be placed at the beginning of the line. Well, even standard code does not always adhere to that rule.

Does this look nice?
When I try to write boolean operators in the beginning of a line I often consider which style should I use A or B?


When I write boolean operators at the end it's almost no question.

Second is alignment of from/order by/group by/index keywords. I prefer indenting them twice for a better readability. But this can be questioned.

Use proper indentation

I follow these guidelines:
  • Indent once where and setting keywords;
  • Do not indent joins. Put them at the same level as first statement (see very last example);
  • Indent group by/order by/index and from (if its moved to a new line) at least once.
    I prefer indenting then twice.
Keep field list compact

Follow the rule: if a field list contains more than 5 fields or it exceeds 100 chars, break it down and use column alignment. Use one column then breaking down.
This rule applies for field list, order by, group by and setting field list.

Make where clause look nice

This one is not an easy task. Especially, then it is a very complex where clause with lots of ANDs an ORs.

Follow these rules:
  • One comparison per line except if it's very short.
  • Then using clauses indent expressions inside by one space except the first one. Keep the first expression in the same line as opening clause "(". Closing clause ")" should be placed at the end of the last expression inside the clause.
  • Rearrange expressions to make it more readable. Try to keep the similar expression close to each other. 
  • Then using NOT operator "!" treat is as part of expression. Don't use special alignment.
  • Then writing AND and OR at the end make them leveled as clauses so the structure can be easily recognized.
Consider this example. Does this look nice?
It's code from standard class BatchRun method serverProcessFinishedJobs

Does this look more readable?

Or maybe this?

You are more than welcome to share your experiences and best practices. Please give a proper reason why you are doing it one or another way.

Be aware and take care!

2015 m. gegužės 15 d., penktadienis

AX Source Code Quality: What is a Bad Source Code?

Hello AX World,

During my short career I have had a chance to see lots (50+) of different AX implementations and solutions from various countries. I have seen both good and bad code. Unfortunately, in most of the cases it was a bad code. And its a problem, because a person who is reading a bad source code can get frustrated quickly.

But what is a bad code? Why it's bad? How to make it better? To answer these questions and to help you to write a better code I am going to start a series of blog post regarding AX source code quality. I am also open for discussions as there are many arguable topics (e.g. code commenting).

I will start my first post with an example to consider:

Keep in mind, this is an overridden method in a class extending RunBase class (from RunBase framework. Ignore the fact that RunBase is an obsolete framework in AX 2012.

How many bad practices can you notice? Before reading it further try to test yourself.
So, how many issues you have noticed? 1, 2, 3,... more?

Let's try with some bad practice basics.

1. Application elements should be started with a capital letter to easily distinguish them among variables.
2. Brackets missing around if statement even if its a single line for a better readability.
3. There should be a single space between addition operators.
4. There should be a single space after a separator.
5. Redundant empty line.

Got the idea? Now lets move on with more sophisticated issues.

6.There should be a single label and preferably strfmt function be used instead  of two labels and a number of adding operators.

7. Instead of calling infolog.add method, error method could be called as it contains the same code and requires only one parameter, the text.

8. Validation logic should be moved to a validate method instead. The method getFromDialog is used to parse values from the dialog after it has been closed by clicking OK. This method has the code which calls validate method afterwards.
9. Validation code could be optimized. Well, this can be arguable.
So I have found 9 problems. Is that a lot for 20 lines of code? Maybe an eagle-eye professional could add even more. All thoughts are welcome.

So to finalize the first post I strongly believe that the code should look like this:

If you can see any issues with my suggested code, feel free to comment.

Be aware and take care!

2015 m. gegužės 14 d., ketvirtadienis

When Collation Takes Over

Hello AX World,

I once had a very interesting issue worth to share. I have recalled it recently when I wrote a SQL script.
This is specifically for databases with the collation Danish_Norwegian_CI_AS.
Consider these two statements:
The first is valid and the second is not. Why? Because AA and aA is treated differently in this collation.

I was running an upgrade (AX 2009 to AX 2012 CU7) and it failed on source environment, live preprocessing stage. It failed on the script:

After spending some time and analyzing it turned out that the issues was due to the DB collation.

The upgrade framework have registered the script as "smAAgreementLinePreUpgradeProcess" but later it searched for "smaAgreementLinePreUpgradeProcess".
See the difference. AX is case insensitive! How could it happen?

As mentioned earlier, AA and aA is treated differently in in Danish_Norwegian_CI_AS collation. AA is converted to a Danish/Norwegian letteÅ and aA is remains aA, therefore the SQL statement could not find what it searched for.

The issues was resolved by making the registered value and search value equal. 

In particular, I had to change one line in method ReleaseUpdateTransformDB50_SMA.initTransformationJobs when it registered the script.

This a good example that a great developer have to consider much more than just it's code.

Be aware & take care! :)

2015 m. vasario 12 d., ketvirtadienis

Valid Time State Key Error after Upgrading or Updating

Hello AX World,

Have you ever had a compilation error “Table does not contain a valid time state key. A unique index needs to be marked as a valid time state key.” and could not realize where it came from? I had… many times and I did not have a clue until one day.

Such errors look strange as they show up on completely standard objects.

I have noticed that they were showing up after upgrades or updates (CUs, hot fixes etc.). That gave me a hint. I know that during an upgrade there are upgrade scripts running to enable or disable certain indexes. So I assumed that these scripts are not behaving well enough. I tried to dig it up, to find scripts that were failing and I have found the problem.


The the index is disabled using indexAllowDup method, then properties AlternateKey and ValidTimeStateKey are reset to default value No.

ReleaseUpdateDB::indexAllowDup(new DictIndex(tableNum(TableName),
indexNum(TableName, DateEffectiveIndex)));

The error starts to show up then indexes are not enabled correctly using method indexAllowNoDup:

ReleaseUpdateDB::indexAllowNoDup(new DictIndex(tableNum(TableName),
indexNum(TableName, DateEffectiveIndex)));

AlternateKey and ValidTimeStateKey properties are not set using the code and then you start getting compilation error mentioned above.

You should use the method indexAllowNoDupAndDateEffective instead:

ReleaseUpdateDB::indexAllowNoDupAndDateEffective(new DictIndex(tableNum(TableName),
indexNum(TableName, DateEffectiveIndex)), ValidTimeStateMode::Gap or NoGap);

Sometimes you can see the following code, but that is implemented in indexAllowNoDupAndDateEffective method:

DictIndex dictIndex = new DictIndex(tableNum(TableName),
        indexNum(TableName, DateEffectiveIndex));
    dictIndex.modify(true, false, true);
    dictIndex.setAlternateKey(true, true);
    dictIndex.setValidTimeStateKey(true, ValidTimeStateMode::Gap or NoGap, true);
    appl.dbSynchronize(dictIndex.tableid(), false);

Recent experience

I have got 3 errors after upgrading from AX 2009 to AX 2012 R3. In earlier versions I had experienced even more…

Tables that there failing:
  1. EximDBKValues_IN (Index: TariffCodeIdx)
  2. EximDEPBScheduleTable_IN (Index: ProductGroupTableRecIdx)
  3. PdsRebateAgreement (Index: SeqIdx)
EximDBKValues_IN table had 4 (!) methods disabling/enabling the same index. The problem was that the incorrect script have been executed the last thus "enabled" the index incorrectly.

  1. ReleaseUpdateDB60_Cust. allowDupEximDBKValues_IN
  2. ReleaseUpdateDB60_Cust. allowNoDupEximDBKValues_IN
  3. ReleaseUpdateDB60_Ledger.allowDupEximDBKValues_INTariffCodeIdx
  4. ReleaseUpdateDB60_Ledger.allowNoDupEximDBKValues_INTariffCodeIdx
EximDEPBScheduleTable_IN table had 2 methods as expected but index enabling was incorrect.
  1. ReleaseUpdateDB60_Ledger.allowDupEximDEPBScheduleTable_INProductG
  2. ReleaseUpdateDB60_Ledger.allowNoDupEximDEPBScheduleTable_INProduc
public void allowNoDupEximDEPBScheduleTable_INProduc()
    ReleaseUpdateDB::indexAllowNoDup(new DictIndex(tableNum(EximDEPBScheduleTable_IN),
        indexNum(EximDEPBScheduleTable_IN, ProductGroupTableRecIdx)));

PdsRebateAgreement table had 2 methods, the same as EximDEPBScheduleTable_IN and enabling index was incorrect.
  1. PmfReleaseUpdateDB60_Pds.allowDupPdsRebateAgreementSeqIdx
  2. PmfReleaseUpdateDB60_Pds.allowNoDupPdsRebateAgreementSeqIdx
public void allowNoDupPdsRebateAgreementSeqIdx()
    ReleaseUpdateDB::indexAllowNoDup(new DictIndex(tableNum(PdsRebateAgreement),
        indexNum(PdsRebateAgreement, PdsRebateAgreementSeqIdx)));

I hope that helps you to save some valuable time to spend on more interesting things.