At some point all developers have the requirement to be able to locate a particular piece of AX object or code. AX does provide some out-of-the-box tools that you could use in order to find Application Object Tree (AOT) entries. Here are the tools and techniques available on how to find AOT objects in AX.

1. The Developer tools

In the AX Developer client under the menu Tools > Model management > Model elements you get a Form-based representation of the top-level Application Object Tree entries, which you could filter by a specific layer, model, type or element name. It is displaying the element from a system view called SysModelElement.

It has a very useful feature on the top: with the “Open new window” button you could jump to the AOT object directly. It is much faster to find and edit a function rather than expanding through the tree and waiting for AX to pull back the list of objects, then start typing to search.

You may also use the Table Browser if you open AOT\System documentation\Tables, and right click > Table browse on a list of tables related to TreeNodes, such as:

  • UtilElements
  • UtilIdElement
  • SysModelElement*

One additional point worth mentioning is that in the same menu the Type hierarchy browser (available under AOT\Forms\SysTypeHierarchyBrowser) can also search for data dictionary objects, however it does not have the best performance to be worth using.

2. Using reflection in AX

Every object in the AOT is called a TreeNode, through which we could manipulate our AOT objects’ properties easily. Most of the methods below fall back to accessing TreeNodes directly or indirectly.

static void WIK_findObjectInAOT(Args _args)
{
    // Check the AOT macro for the path names
    #AOT

    SysModelElement         sysModelElement;

    // If you know what is the object you are looking for then use TreeNode
    TreeNode::findNode(#TablesPath + #AOTDelimiter + identifierStr(CustTable))
        .AOTnewWindow();

    // You can fall back to TreeNode from the SysDict* objects
    new SysDictTable(tablenum(SalesTable))
        .treeNode().AOTnewWindow();

    // If you do want to do a wildcard search you could just use the SysModelElement view
    while select sysModelElement
        where sysModelElement.Name like '*SalesId*'
    {
        info(strFmt('(%1) - %2',
            enum2Symbol(enumNum(UtilElementType), any2int(sysModelElement.ElementType)),
            SysTreeNode::modelElement2Path(sysModelElement)));
    }
}

The next example will demonstrate how to iterate through the AOT, for which you may use the TreeNodeTraverser and TreeNodeIterator classes. It will find a specific search string within your AX projects.

static void WIK_findObjectInProject(Args _args)
{
    str                     findObjectName;
    Dialog                  dialog;
    DialogField             dialogField;

    boolean                 found           = false;
    TreeNodeIterator        iterator        = SysTreeNode::getSharedProject().AOTiterator();
    TreeNodeTraverser       tnt;
    ProjectNode             projectNode;
    TreeNode                treeNode;
    SysOperationProgress    progress        = new SysOperationProgress();
    int                     projCount       = 0;

    dialog = new Dialog();
    dialogField = dialog.addField(identifierStr(SysAotControlName));
    dialogField.control().mandatory(true);

    if (dialog.run() && dialogField.value() != '')
    {
        findObjectName = dialogField.value();
    }
    else
    {
        return;
    }

    while (iterator.next())
    {
        projCount++;
    }

    iterator.reset();
    progress.setTotal(projCount);

    setPrefix('Find object in project');
    setPrefix(findObjectName);

    try
    {
        projectNode = iterator ? iterator.next() : null;

        while (projectNode)
        {
            progress.incCount();
            progress.setText(projectNode.AOTname());
            tnt = new TreeNodeTraverser(projectNode.loadForInspection());

            while (tnt.next())
            {
                treeNode = tnt.currentNode();
                // Stop on first hit
                if (treeNode && strupr(treeNode.AOTname()) == strupr(findObjectName))
                {
                    found = true;
                    break;
                }
                treeNode.treeNodeRelease();
            }

            if (found)
            {
                info(strFmt('Project: %1 - First hit: %2', projectNode.AOTname(), tnt.currentNode().treeNodePath()));
                treeNode.treeNodeRelease();
                found = false;
            }

            projectNode = iterator.next();
            //projectNode.treeNodeRelease();
        }
    }
    catch
    {
        throw error(strfmt("Error during project iteration on node %1", projectNode.AOTname()));
    }

    progress.kill();
}
2. The Model store approach

The whole AOT is represented in a separate set of tables within your AX database. As of the most current version they live separately. Data is in your main database, metadata and code lives in the _model database. Source code is only visible in pcode / binary format, but the treenodes and labels are accessible via the Model* tables and SysModel* views.

Here is an example on how to find that which tables have the field SalesId in SQL Server Management Studio:

USE AX2012Upgrade_model;
GO

SELECT
	ModelElement.ElementType, ModelElement.ElementHandle, ModelElement.Name, ModelElement.Origin,
	ModelElementData.CREATEDDATETIME, ModelElementData.CREATEDBY, ModelElementData.MODIFIEDDATETIME,
	ModelElementData.MODIFIEDBY, parent.ElementType AS [Parent type], Parent.Name AS [Parent name]
	FROM ModelElement
	INNER JOIN ModelElementData
		ON ModelElementData.ElementHandle = ModelElement.ElementHandle
	LEFT OUTER JOIN ModelElementData AS ParentData
		ON ParentData.ElementHandle = ModelElement.ParentHandle
	INNER JOIN ModelElement AS Parent
		ON Parent.ElementHandle	= ParentData.ElementHandle
	WHERE ModelElement.ElementType = 42 -- UtilElementType == Tables
		AND ModelElement.Name LIKE '%SALESID%'
		AND ModelElementData.LayerId > 5 -- Customizations only. Values are in Layer table

It will return the customized SalesId fields nicely:

findaotinmodelstore

3. Project filtering

If you create a new Shared/Private project, you may use the Advanced Filter/Sort <Ctrl+F3> functionality. There you may choose AOT grouping, do a search by exact object name or using a wildcard, only include specific UtilElementTypes, or include objects existing on a concrete layer/model only. It is a very versatile and fast tool for quickly pulling together elements that you need.