All posts by Rikard Gadolin

Cannot add or remove static keyword for this method

A while ago while delivering code for a customer (in Microsoft Dynamics AX 2012) I ran in to this problem while compiling with axbuild.exe (but not while compiling in the aot).
I haven’t been able to reproduce this error after fixing the problem, but from what I read there might be several ways to reproduce it.

So, how did I fix this?
First things first.. What does the error message mean and why does it only appear while using axbuild.exe?
Since last compilation of the object the static keyword on the method you are trying to compile has been removed or added.
When the static keyword is being added/removed you are note only changing the plain text code of the method, but also the type of the object.

The type of an aot object is stored in the model database in the table “ModelElement”.
As you can see there is a field named “ElementType”.
2016-05-30 16-52-35

And here is Microsoft’s list of types.

The problem when you get this error message is that the element type is not in sync with the plain text code.
For example: Your method is saved as a static method, but in the “ModelElement” table the element type is still 12 (TableInstanceMethod) where it should be 22 (TableStaticMethod).
For some reason the compiler in the aot does not validate this, but the axbuild.exe compiler does.

Here is a comparison I did between the source and destination environment during my installation of code.
5FFA24F0

Changing this directly in sql with “Sql Server Management Studio” will do the trick.

Sales line number not unique

There are cases in all current versions of Microsoft Dynamics AX where the line number on sales order lines is not incrementally assigned and can therefore be assigned in duplicates.

For the purpose of the post i have added the field “Linenum” to the grid in the sales order form.

As you can see below the line numbers are assigned incrementally when creating order lines in an ordinary fashion. (one by one).
2016-05-27 10-35-15

Some sellers might decide to add a few empty sales lines to the order before picking the item number like this.
2016-05-27 10-44-41

At this point the line number still looks normal.
But lets see what happens when when we start picking values for these lines and saving them.
2016-05-27 10-48-05

In this example i start by assigning an item number for the last row and then for the first row.
As soon as the item number is assigned and i leave the field, the line number is changed to 3.

The first thing you might ask yourself is: “Why the heck would you create order lines this way?”
That i can’t answer, but i know for sure that it’s not totally uncommon.

If sales line number are not unique you might run in to problems with some customizations that uses the line number to identify the sales line.

Our solution for this is to enforce a more “normal” behavior by adding a piece of code that deletes all cached sales lines every time a sales line is saved.

Simply add this method to the data source “SalesLine” in the form “SalesTable”.

private void HandleMultipleCacheLines()
{
    SalesLine   salesLineLineNum;
    ;
 
    salesLineLineNum = salesLine_ds.getFirst();
 
    while (salesLineLineNum.SalesId)
    {
        if (!salesLineLineNum.RecId)
        {
            salesLine_ds.cacheRemoveRecord(salesLineLineNum);
        }
 
        salesLineLineNum = salesLine_ds.getNext();
    }
 
    salesLine_ds.reread();
    salesLine_ds.refresh();
}

And call the new method from within the method “write” on the data source “SalesLine”.
Put the call right below “super();”.

Please let me know if you have a another solution.

Hide the Content Pane in AX 2009

For developers the ‘Content Pane’ can contribute to a great deal of frustration.

Specially for does who are used to develope in AX 4 or earlier.

Here is a small job that will hide it for you:

1
2
3
4
5
6
7
8
9
10
11
12
static void Job1(Args _args)
{
    #WinAPI
    ;
 
    WinApi::showWindow(
        WinApi::findWindowEx(
            WinApi::findWindowEx(
                WinApi::findWindow('AxMainFrame', ''),
                    0, 'MDIClient', ''),
                        0, 'ContentFrame', ''), #SW_HIDE);
}

Replace #SW_HIDE with #SW_SHOW to show it again.

Using Regular Expressions in Dynamics AX

When working with strings, ex. replacing text, validating content or simply checking for existence, It can sometimes be a good idea to consider using Regular Expressions (RegExp), insted of using ordinary string manipulation methods.

X++ itself does not seem to support Regular Expressions, but we can use the .NET functionality.

Below is an example method of how to validate that an email address is written in a proper format.

1
2
3
4
5
6
7
8
9
10
11
Static Server boolean validateEmail(EMail   _eMail)
{
    Str MatchEmailPattern =
    @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$";
    System.Text.RegularExpressions.Match myMatch;
    ;
 
    myMatch = System.Text.RegularExpressions.Regex::Match(
    _eMail, MatchEmailPattern);
    return(myMatch.get_Success());
}

It simply returns true if the input is a properly formatted email.

To replace text in a string you could do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void Job1(Args _args)
{
    Str EmailPattern =
    @"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b";
    Str ReplacedText;
    ;
 
    ReplacedText =
    System.Text.RegularExpressions.Regex::Replace(
    "My email address is: example@haxx.net",
    MatchEmailPattern,"blabla@haxx.net");
 
    print ReplacedText;
    pause;
 
}

This will replace ALL found email addresses.

Below is an example of how to “find” all matches.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void Job6(Args _args)
{
    Str MatchEmailPattern =
    @"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b";
    System.Text.RegularExpressions.Match myMatch;
    ;
 
    myMatch = System.Text.RegularExpressions.Regex::Match(
    "Your email: youremail@host.net, and my"
    +"email: myemail@anotherhost.com", MatchEmailPattern);
 
    while (myMatch.get_Success())
    {
        print myMatch.get_Value();
        myMatch = myMatch.NextMatch();
    }
 
    pause;
}

Notice the difference between the RegExp pattern in this example and the first example.

The first pattern checks if the input is valid.

And this pattern checks for ALL valid matches.

For more information about regular expressions visit:
www.regular-expressions.info