Tuesday, November 18, 2008

10 tips for debugging in Dynamics Ax

Fixing bugs requires quite a bit of experience and knowledge of the modules involved, both on a technical and functional level. The first step to fix something is to find the cause of the problem, a.k.a. debugging.

You shouldn’t limit yourself to using the debugger when things go wrong. Debugging can help you understand the system. I often fire up the debugger just to see what happens in a standard application. This helps me to see how modifications can be implemented and what the consequences are. Dynamics is too big and too complex to be able to just dive in and change something.

Here are some tips to help you in the fine art of debugging. Some might be blatantly obvious to experienced developers. These are things I wish I had known when I first started working with Axapta.

Assume you broke it
This is probably the most important advice. We developers tend to think we write good code. Some of us do, some of us don’t. But nobody does it flawlessly. By default, assume anything you didn’t write yourself works perfectly. This narrows down the search considerably. After careful debugging you may come to a different conclusion. In which case you’ll have a good bug report to file.

If a system has been running fine for a while and it suddenly breaks down after importing new code, those changes are likely to be the root cause of the problem. Try reverting your changes and doing the exact same thing. If the problem remains, you have found an unrelated problem. If not, you know where to start looking for errors.

Get a clear description of the problem
Unless the error is clear enough and you immediately know how to fix it, you’ll need a detailed description how to trigger this error. Unfortunately this can be very hard. Getting users to tell you exactly what you need to understand a bug isn’t that simple. Keep in mind that users are generally not interested in the program they’re using. They just want to get their job done. They have been taught to use the system in a certain way and unexpected errors confuse them. They might not realize what’s different when things go wrong compared to when everything just works.

You need to ask the right questions. If necessary sit next to them and watch them work. Take notes and try to notice special cases. And don’t forget to ask what the correct behaviour should be. There may be no error message and whatever happens may look correct but the user could be expecting a different result.

Without a good scenario it may be impossible to solve some bugs.

Don’t worry to much about errors that only occur once
If something goes wrong only once and it doesn’t happen again, don’t worry too much about it. Depending on the risk it may be better to fix the damage and move on. There’s probably a bug lurking somewhere but you have to decide if it’s worth chasing it.

Intercept error messages
Anything sent to the info log window passes through the add() method on the Info class. Put a breakpoint there if you want to know where a message is triggered. Using the stack trace in the debugger it’s usually not that hard to see which conditions cause it.

Often it turns out to be a missing setting in one of the basic tables.

Intercept data modifications
Not all bugs come with an easy to intercept error message. Sometimes all you get is bad data. It’s possible to see when and why records are created, modified or deleted by putting breakpoints in insert(), update() or delete() on a table. Create them if necessary. Just being able to look at the stack in the debugger when these are called can be very insightful.

Remember that it is possible to modify data without passing through these methods. Like using doInsert(), doUpdate() or doDelete(), or using direct SQL. It’s not very common but sometimes you can miss something.

Intercept queries
If you suspect a query is not correct you’ll want to verify its output. A way that doesn’t require much work is using the postLoad() method. It can be overridden on each table and is called for each selected record. It even works with complex joins. Putting an info() in the postLoad() of each table in a query can tell you a lot about what’s happening.

The cross-reference is your friend
The cross-reference is one of the most important tools when developing and debugging in Dynamics Ax. Always try to have an environment somewhere with an updated cross-reference (not the live environment). You can find the cross-reference in the development tools menu.

Need to know where a field gets its value? The cross-reference tells you where every read and write happens.
Want to know where an error message is used? Open the label editor and find the label, then click the Used By button.

Set up a separate environment
When dealing with complex problems it helps to have a separate environment for debugging. This allows you to freely modify code and data without affecting the live system. This is very important when you have to post invoices or do anything else that is basically irreversible.

It also prevents live users from being blocked if you have breakpoints in the middle of a transaction.

Dealing with large datasets
Sometimes a problem can only be reproduced in (a copy of) the live environment. You’re often stuck with a lot of data that doesn’t matter but gets in the way. Like when you need to debug the MRP. Using regular breakpoints doesn’t help because it takes too long before you get to the real issue.

In this case you need to have some more tricks up your sleeve to narrow down the search. One option is to work in several passes. Using the cross-reference determine places where something interesting happens and dump data with info() or Debug::printDebug(). This should narrow down the possible suspects. With a bit of luck just looking at the data can be enough to identify the problem.

Another way is implementing your own conditional breakpoints. The debugger doesn’t offer these out of the box but you can roll your own with an if-statement and the breakpoint statement. This is very effective if you have some more or less unique identifier of the problem, like a record ID or a customer account or even a date.

Clean up
Don’t forget to remove any modifications you made while debugging. You probably don’t want to leave a hardcoded breakpoint in a live system. Been there, done that, very annoying.

Good luck hunting for bugs

The mystery of "index" vs. "index hint"

In the Axapta community, there is still a big confusion about the "index" and "index hint" statements used in connection with selects.

So, what is the Axapta kernel *really* doing:

Using "index": when you add the statement "index MyIndex", the Axapta kernel will add an "ORDER BY" with all the fields of the index.

Example: select * from InventTable index GroupItemIdx will generate the following SQL statement to the database:


The Index ItemGroupIdx of the InventTable exactly contains the two fields ItemGroupID and ItemId (in that order). Using "index", you still give the control of which index to use to the database optimizer. So, if the optimizer finds a better index to use, it will use it.

Using "index hint": when you add the statement "index hint MyIndex", the Axapta kernel will add a statement to instruct the database to use that index and no other one.

Example: select * from InventTable index hint GroupItemIdx will generate the following SQL statement to the database:


Using "index hint", you take away the control of which index to use from the database optimizer. So, if there may be a better index, the database will not use it.


Adding the "index" statement to an Axapta select, it does NOT mean that this index will be used by the database. What it DOES mean is that Axapta will send an "order by" to the database.

Adding the "index hint" statement to an Axapta select, it DOES mean that this index will be used by the database (and no other one).

This rule applies to both the MSSQL and Oracle databases.

Where Breakpoints are stored for Axapta

If you ever wondered where Axapta stores the breakpoints, here is the answer:
breakpoints are stored in the registry under the key


Here's a screenshot:

Some facts about AOS clusters

For your information:
• When you have a SQL cluster for failover and the actual failover happens, the AOS will lose the connection to the SQL server and you have to restart the AOS service.

• When you have an AOS cluster and you set it to start "On demand", it will not work. If the AOS instances are not running they will never start when clients try to connect. Sad but true. Maybe this will change when we get the new Dynamics Ax 4.0.

Create a new method in runtime

Some one may need to use codes to create or edit a method in Axapta.
Here is an example to show how to create a lookup method for a form's field in runtime.

static void CreateFieldMethod(Args _args)
TreeNode tn1,tnAddr, methodsNode;
MemberFunction memberFunction;
str source;

//Thanks for Max Belugin's comments,
//it is really good to use Verbatim String as well.
//The reason why I use escape characters here is
//because this line of code is copied from standard Axapta application :)
tn1 = infolog.findNode("\\Forms\\Address\\Data Sources\\Address\\Fields\\AddrRecId");

tnAddr = infolog.findNode( "\\Forms\\Address" );
methodsNode = tn1.AOTfindChild( 'Methods' );
memberFunction = methodsNode.AOTfindChild( 'lookup' );
source = @"public void lookup(FormControl _formControl, str _filterStr)
super(_formControl, _filterStr);
}" ;
memberFunction.AOTsetSource(source, false);

'Not Like' in Dynamics AX

In X++, we can use Like '*someIdentifier' to implement the Like keyword.
select firstonly purchTable
where purchTable.purchId like '00007*';

However if you want to use 'Not Like' in X++ SQL statement, you have three options:
The first option, using '!' as 'not',
select firstonly purchTable
where !(purchTable.purchId like '00007*');

The second option, using notExists join
PurchTable purchTable, refPurchTable;

select firstonly purchTable
notExists join refPurchTable
where purchTable.purchId == '00007*';

Please make sure that you do put purchTable.purchId in condition statement, otherwise the SQL statement will retrieve an empty result set.
The last option, using Query
Query query = new Query();
QueryRun queryRun;

query.addDataSource(tableNum(PurchTable)).addRange(fieldNum(PurchTable, PurchId)).value('!00007*');
queryRun = new QueryRun(query);
purchTable = queryRun.get(tableNum(PurchTable));
print purchTable.PurchId;

Bye bye, axdat.udb !!!

One of the very good things about Dynamics AX 4.0: there is no axdat.udb file anymore! All information about users online etc... is kept in the database.

The table that holds the users' data is called SYSCLIENTSESSIONS.
The table that holds the AOS' data is called SYSSERVERSESSIONS.

It seems to me that SysClientSessions will contain the sessions that had been logged in on the current day (no matter if they are still logged in or not). Currently logged in sessions will have the field STATUS set to 1.

DAX 4.0 Clustering question

"Wouldn't the session manager take care of directing a request to a certain
(I.e. load balancing).When not using the session manager, how is
determined which AOS in the cluster to use ?"

Well: I can only assume that it makes the same as Axapta 3.0 (but better): the client tries to connect to all AOS servers in it's configuration. Each AOS server tells the number of users connected, the client connects to the one with the lowest user count

In Ax 4.0 SP1, the new "session manager" for AOS clustering was introduced (so you do not need NLB anymore). This, however, means a new single point of failure, because if the session manager crashes, clients will not be able to login to AX anymore.
The good news, though, is that you do not need the session manager!

Make the following setup: several instances of AX, mark all with the flag "Make this AOS instance part of the load balancing cluster". In the client config, add all your AOS instances on the various servers. The client will connect to one of the available servers. And the good thing is: this setup will work, even if one or some of the AOS instances are not running (this did not work in Axapta 3.0: there the client would have said that it cannot connect to the AOS server).

Thursday, October 30, 2008

Wednesday, October 15, 2008

AX2009 layers and Architecure (Short Note)

You can consider AX layers anologous to transparent OHP slides placed on top of each other. Each slide has a name, range and a purpose. In AX 2009, the layers are (low to high)

USR User - Individual companies or companies within an enterprise can use this layer to make customizations unique to customer installations.

CUS Customer - Companies and business partners can modify their installations and add the generic company-specific modifications to this layer.The layer is included to support the need for in-house development without jeopardizing modifications made by the business partner.

VAR Value-added reseller - Business partners use this layer, which has no business restrictions, to add any development done for their customers.

BUS Business solution - Business partners develop and distribute vertical and horizontal solutions to other partners and customers.

SL1-SL3 Certified solutions - Partners certified under the Microsoft Dynamics Industry Solution (MDIS) program distribute their solutions in the SL layers.

HFX Hotfix - The Dynamics AX team delivers critical hotfixes using the HFX layer.

GLS Global solution - The Dynamics AX Global Development and Localization team provides a set of GLS layers that contain country-specific functionality for regions in which Dynamics AX is released.

SYS System - This is the lowest model element layer and the location of the standard Dynamics AX application. Only Microsoft has access to the element definitions at this layer.

Objects defined at higher levels of the stack override objects defined at lower levels of the stack. Means a USY Layer Object will cast a shadow on the same object in say VAR Layer. Objects are stored in a separate file (*.aod) on each layer whenever they are saved from the MorphX development environment client. Element definitions are read from these files and dynamically composed by the Dynamics AX runtime. Object instances are created on either the server or the client based on the model element definition.

Tuesday, October 14, 2008

About Finance Dimension

Dimensions have the following characteristics:
• Provide a more detailed description of a transaction.
• Simplify the accounting process when you enable detailed analysis without the creation of a detailed Chart of Accounts.
• Resemble separate accounts for:o Departmentso Cost centerso Purposes
• Are defined to analyze and classify financial transactions as follows:
Financial transactions usually are organized according to voucher and account number. Additional grouping and classification options are available when you assign several dimensions to transactions.
• Enable you to view the data file from different perspectives to improve the effectiveness in tracking figures across accounts.
• Can be used throughout the system.
• Add dimensions to base data such as: Ledger accountso Customerso Vendors
• Are copied to transactions automatically. You can have max 10 dimensions in AX ( 3 are default). I'll take a simple example. Lets say you have a ledger account 100100 as Telephone Expenses and you have 2 departments (Sales & Purchase). You want to see all expense department wise. One way would be to have 2 Ledger Account Codes as 100100-1 as Telephone Expenses - Sales & 100100-2 as telephone Expenses - Purchase . However this would be inconvenient with larger departments and larger analysis criteria. Hence using dimensions (Sales & Purchase) and posting to same account 100100 will help you get expenses department wise. Help you keep a managable CoA.

Wednesday, September 24, 2008

Documentation on India Localization on Ax 4.0 SP2



The white papers are available on following links : Public Site http://mbs.microsoft.com/public/axwhitepapers

Partnersource https://mbs.microsoft.com/partnersource/documentation/whitepapers/axwhitepapers.htm?printpage=false

Customersource https://mbs.microsoft.com/customersource/support/documentation/whitepapers/axwhitepapers.htm?printpage=false

What are ADO files ?

ADO stands for Application Data Object and holds the application code for alayer. You have an ADO file for each layer you have modified in your system.Per defaul it is locaed in C:\program files\Microsoft DynamicsAX\40\Application\Standard . There will be at least a axSYS.aod and anaxSYP.aod . Where the axSYS.aod is the SYS layer and axSYP.aod the SYS-Patch(SYP) layer.

Question :: When using index (not using hint) in a select statement without 'GroupBy' or 'OrderBy' in what sequence do the records occur

When using index (not using hint) in a select statement without 'GroupBy' or'OrderBy' in what sequence do the records occurAnswer:When using Index hint, the index chosen is hinted to the SQL server as theone that should be used for the query.(you shouldn't use this unless you are very sure that's the best selectiveindex there ever will be for the table)the index (without hint) is interpreted into a simple ORDER BY clause, sothe records will be ordered in the order of the fields from the index.( you can turn on logging SQL statements and see the actual query being sentto the server)

Question : I have a system with a clean installation of AX , after I change the domain I am not able to run AX. So i have to reinstall the AX.

I solved it by editing the UserInfo table, replacing theNetwork user, Network domain and SID fields with values from a user in thenew domain.