In Microsoft Dynamics AX (X++), there are many scenarios where you need to duplicate data from one table to another—especially during custom import, duplication, or versioning processes.
Traditionally, this is done by manually assigning each field:
targetTable.Field1 = sourceTable.Field1;
targetTable.Field2 = sourceTable.Field2;
...
But what if the tables have many fields? Or maybe you’re dealing with multiple similar table pairs?
That’s where the powerful DictTable
class comes in. Let’s walk through how to use it to copy matching fields dynamically between two tables.
💡 Use Case: Copy Customer Templates
Let’s assume you have these tables:
-
CustTemplateHeader
– stores predefined customer templates. -
CustTemplateHeaderHistory
– a historical copy of templates for versioning.
You want to copy records from CustTemplateHeader
to CustTemplateHeaderHistory
, but only for fields that exist in both tables.
✅ The Solution Using DictTable
SalesHeaderTemplate salesHeader;SalesHeaderHistory salesHeaderHistory;SalesTemplateGroup templateGroup;SalesTemplateGroup templateGroup_TDS;
while select salesHeader where salesHeader.TemplateGroupId == templateGroup.RecId{ salesHeaderHistory.clear();
DictTable dictFrom = new DictTable(tableNum(SalesHeaderTemplate)); DictTable dictTo = new DictTable(tableNum(SalesHeaderHistory)); int fieldId;
for (fieldId = dictFrom.fieldNext(0); fieldId; fieldId = dictFrom.fieldNext(fieldId)) { str fieldName = dictFrom.fieldName(fieldId); FieldId toFieldId = dictTo.fieldName2Id(fieldName);
if (toFieldId) { anyType value = salesHeader.(fieldId); salesHeaderHistory.(toFieldId) = value; } }
salesHeaderHistory.RecId = 0; salesHeaderHistory.TemplateGroupId = templateGroup_TDS.RecId; salesHeaderHistory.insert();}
🧠 Why This Approach Rocks
-
✅ No need to hardcode fields
-
✅ Automatically adapts to schema changes
-
✅ Great for data versioning or duplication logic
-
✅ Scales across multiple similar table pairs
⚠️ Points to Remember
-
This technique only works for matching field names and compatible data types.
-
You may want to exclude system fields like RecId
, CreatedDateTime
, etc.
-
Set RecId = 0
to prevent conflicts and let the system assign a new one.
🔁 Bonus Tip: Turn It Into a Reusable Function
You could even encapsulate this logic into a utility method or static class so it can be reused across your project.
🔧 Reusable Utility Class: TableCopyHelper
📁 Step 1: Create a new class
Create a new class in AOT or through Visual Studio, name it something like: TableCopyHelper
.
public class TableCopyHelper
{
/// <summary>
/// Copies matching fields from one table buffer to another.
/// Skips the RecId field to avoid duplicate key issues.
/// </summary>
/// <param name="source">Source table buffer</param>
/// <param name="target">Target table buffer</param>
/// <param name="preserveRecId">If false, clears the RecId to force insert as a new record</param>
public static void copyMatchingFields(Common source, Common target, boolean preserveRecId = false)
{
DictTable dictFrom = new DictTable(source.TableId);
DictTable dictTo = new DictTable(target.TableId);
FieldId fieldId;
FieldId recIdFieldId = fieldNum(Common, RecId);
for (fieldId = dictFrom.fieldNext(0); fieldId != 0; fieldId = dictFrom.fieldNext(fieldId))
{
str fieldName = dictFrom.fieldName(fieldId);
FieldId toFieldId = dictTo.fieldName2Id(fieldName);
// Skip if no matching field in target
if (toFieldId == 0)
continue;
// Skip RecId field explicitly
if (toFieldId == recIdFieldId)
continue;
anyType value = source.(fieldId);
target.(toFieldId) = value;
}
if (!preserveRecId)
{
target.(recIdFieldId) = 0;
}
}
}
Step 2: Use It in Your Code
Example: Copy from SalesHeaderTemplate
to SalesHeaderHistory
SalesHeaderTemplate salesHeader;SalesHeaderHistory salesHeaderHistory;
while select salesHeader where salesHeader.TemplateGroupId == templateGroup.RecId{ salesHeaderHistory.clear();
TableCopyHelper::copyMatchingFields(salesHeader, salesHeaderHistory);
salesHeaderHistory.TemplateGroupId = templateGroup_TDS.RecId; salesHeaderHistory.insert();}
🚀 Benefits
-
✅ Reusable for any pair of similar tables.
-
🛡 Prevents accidental copying of RecId
, timestamps, or AutoIdentification
fields.
-
🧼 Cleaner calling code with less repetitive logic.
-
📈 Easier maintenance — any future logic updates are made in one place.
Comments
Post a Comment