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:
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.