In Microsoft Dynamics AX 2012 we have solved the element id problem. The solution is outlined in this post. The solution consists of major changes both having an impact on existing AX 2009 and AX 4 solutions:

  1. Element IDs (like class ID and table ID) are now 32 bit.
  2. Element IDs are installation specific. The same class will have different IDs on different installations.

When making IDs 32 bit we had to deprecate the TypeId() function. TypeId() took one argument the name of an Base Enum or Extended Data Type. And it returned an integer value, where the upper 16 bits were the ID, and the lower 16 bits denoted if the type was a Base Enum or an Extended Data Type. For example: TypeId(MyEDT) returned 0xC351 000B = (50001 << 16)+11. Naturally such a function wouldn't work with 32 bit IDs. It turned out, that we didn't need a replacement for the TypeId() function, as the name of Base Enums and Extended Data Types are unique across both types. The solution then simply become to update the framework methods that accepted the combined ID from TypeId() with a new name based implementation. And to make it better, it turned out that usage of TypeId() was restricted to very few frameworks. The table below shows a complete list of the usage patterns of TypeId() we found in the SYS layer, and how we updated them. When you are upgrading the compiler will detect all reference to TypeId() for you.

Dynamics AX 4/2009              Dynamics AX 2012
// Dialog related
// When adding fields to a dialog, the types are now identified by Name – not ID.
dialog.addField(typeid(MyEdt));             dialog.addField(extendedTypeStr(MyEdt)); 
dialog.addField(typeid(MyEnum));            dialog.addField(enumStr(MyEdt)); 
dialog.addFieldValue(typeid(MyEdt));        dialog.addFieldValue(extendedTypeStr(MyEdt)); 
dialog.addFieldValue(typeid(MyEnum));       dialog.addFieldValue(enumStr(MyEdt)); 
new DialogField(dialog, typeId(MyEdt));     new DialogField(dialog, extendedTypeStr(MyEdt)); 
new DialogField(dialog, typeId(MyEnum));    new DialogField(dialog, enumStr(MyEnum)); 
dialog.addField(types::string);             dialog.addField(identifierStr(str)); 
dialog.addFieldValue(types::string);        dialog.addFieldValue(identifierStr(str)); 
new DialogField(dialog, types::string);     new DialogField(dialog, identifierStr(str)); 

// Dict related
DictField.extendedTypeID() == typeid(MyEdt) DictField.typeID() == extendedTypeNum(MyEdt) 
DictType.extendedTypeID() == typeid(MyEdt) == extendedTypeNum(MyEdt) 

// Args related
args.parmEnumType(typeId(myEnum))           args.parmEnumType(enumNum(myEnum)) 

//Type related
typeId2Type(typeId(myEdt))                  typeName2Type(extendedTypeStr(myEdt)) 

// Other patterns with better alternatives
// These alternatives can also be used in AX 4 / 2009
typeId(myEdt) >> 16                         extendedTypeNum(MyEdt) 
typeId(myEnum) >> 16                        enumNum(MyEnum) 
typeId2ExtendedTypeId(typeId(myEdt))        extendedTypeNum(MyEdt) 

As element IDs now are installation specific then we need to provide enough information for data export/import to convert the data correctly between the source and target system. There are 3 things to do:

  1. Do not store IDs in containers,

    If IDs are stored in a non-relational format (like a container), then data export/import cannot identify them, and the IDs will be imported as-is; which means they will resolve incorrectly on the target system. A best practice rule is available to identify IDs in containers.
  2. Use the right Extended Data Types on your fields (TableId, FieldId, ClassId, etc.)

    Data export will convert data using these Extended Data Types into a format that can be correctly resolved on the target system.
  3. For fields containing field IDs
    1. Set the RelatedTable property, OR
    2. Create a new field group with the field referencing the table and the field referencing the field.

For data export/import to be able to correctly convert a field ID, it is required that you specify which table the field belongs to. If the field always belongs to the same table, you can use the RelatedTable property to specify it (3.1), OR if the field ID is referencing a field in an arbitrary table, you need to link the table reference and the field reference to each other. You do this by creating a new field group containing the two fields (3.2)