Wednesday, June 18, 2014

[AX 2012 Upgrade] Identifying bad EDT relations using reflection and creating custom project with them.

In AX 2012, relations are no longer supported under Extended Data Types.

During an upgrade where you are re-implementing, it's common to import database schema via XPO to get your table structure with EDTs.

Sometimes EDTs that are brought over end up pointing to non-existent tables and/or and you change/rename tables, things get broken.

To uplift your old EDT relations to their corresponding tables, you can use the EDT Relation Migration Tool ( located under Tools>Code Upgrade>EDT Relation Migration Tool.

My first time running this tool, it errored with "The table  does not exist."

Which was due to an EDT that was imported that had a broken table relation.

This job will loop over every CUS+ EDT, and if it has a relation, check if it's valid.  If it is not, it will create a private project of the EDTs with errors.  You can easily change it to search different layers/models/etc.  I only limited to the CUS layer for performance.

static void JobCreateProjWithBrokenEDTs(Args _args)

    SysProjectFilterRunBase     projectFilter = new SysProjectFilterRunBase();
    ProjectNode                 projectNode;
    UtilElements                utilElements;

    SysModelElement             modelElement;
    SysModelElementType         modelElementType;
    SysModelElementData         modelElementData;

    SysDictType                 sysDictType;
    SysDictRelation             sysDictRelation;
    TableId                     tableId;
    SysDictTable                sysDictTable;
    SysDictField                sysDictField;
    FieldName                   fieldName;
    int                         i;

    void addBadEDT(SysModelElement _modelElement)
        utilElements = null;
        utilElements.Name = _modelElement.Name;
        utilElements.ParentID = _modelElement.ParentId;
        utilElements.RecordType = _modelElement.ElementType;

        if (utilElements.RecordType == UtilElementType::SharedProject ||
            utilElements.RecordType == UtilElementType::PrivateProject ||
            utilElements.RecordType == UtilElementType::ClassInternalHeader ||
            utilElements.RecordType == UtilElementType::TableInternalHeader ||
            info(strfmt("@SYS316339", strfmt('%1 %2', utilElements.RecordType, utilElements.Name)));


    while select modelElement
        join Name from modelElementType
            where modelElementType.RecId == modelElement.ElementType    &&
                  modelElementType.RecId == UtilElementType::ExtendedType
        join modelElementData
            where modelElementData.ModelElement == modelElement.RecId   &&
                  modelElementData.Layer        >= (UtilEntryLevel::cus-1)
        sysDictType = new sysDictType(modelElement.AxId);

        if (sysDictType)
            sysDictRelation = sysDictType.relationObject();

            if (sysDictRelation)
                tableId = sysDictRelation.table();

                sysDictTable = new SysDictTable(tableId);

                if (sysDictTable)
                    // Found an EDT with a valid table, check if the field
                    // relations are good
                    for (i = 1; i <= sysDictRelation.lines(); i++)
                        fieldName = fieldid2name(tableId,sysDictRelation.lineExternTableValue(i));

                        sysDictField = new SysDictField(,fieldname2id(,fieldName));

                        if (!sysDictField)
                            // Field relation is bad on EDT
                            warning (strFmt("%1 found table, missing field on table %2",,;

                    // Found an EDT with a broken table relation
                    warning(strFmt("%1 missing valid table",;

    SysUpgradeProject::delete(#ProjName, ProjectSharedPrivate::ProjPrivate);
    projectNode = SysTreeNode::createProject(#ProjName);

    info(strFmt("Created private project %1", #ProjName));

1 comment: