There are plenty of problems in AX 2012 in synchronising elements with TFS, especially when multiple developers are working on isolated environments and adding new code frequently.
This post will show you how to resolve most of the problems occurring after TFS synchronisation. Initially you should run synchronisation from development workspace from version control menu.
Problem 1
After synchronisation you should check for any elements that are not processed. You can check that from the ‘Synchronization log’ form or directly in the table ‘SysVersionControlSynchronizeLog
’. Those elements that are not processed means that they are not imported correctly in your environment. The standard feature to process these is to run process from ‘Synchronization log’ form but the problem is that this processing routine tries to process the elements based on their batch id which is actually a group id. So the problem occurs when there are multiple elements with same batch id (Synchronisation). So there is a workaround to resolve this like update the batch id’s per type of elements i.e. check for last batch id and then start updating the elements e.g. for forms add batch id ‘3’. This can be cumbersome if you have plenty of not synchronised elements so for this reason I wrote a simple job that will easy the process:
static void updateNotSynced(Args _args)
{
SysVersionControlSynchronizeLog syncLog;
SysVersionControlSynchronizeBatchNum lastNum;
boolean entered = false;
//find last batch num
select firstOnly BatchNum from syncLog
order by syncLog.BatchNum desc;
lastNum = syncLog.BatchNum;
setPrefix('Numbers to process:');
//classes
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Classes*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for classes is: %1', lastNum));
}
//forms
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Forms*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Forms is: %1', lastNum));
}
//tables
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Data dictionary*Tables*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Tables is: %1', lastNum));
}
//extended data types
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Data dictionary*Extended data types*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for EDTs is: %1', lastNum));
}
//base enums
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Data dictionary*Base enums*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Enums is: %1', lastNum));
}
//menu items
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Menu items*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Menu items is: %1', lastNum));
}
//shared projects
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Projects*Shared*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Shared projects is: %1', lastNum));
}
//jobs
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Jobs*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Jobs is: %1', lastNum));
}
//SSRS Reports
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*SSRS Reports*Reports*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for SSRS reports is: %1', lastNum));
}
//Security roles
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Security*Roles*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Roles is: %1', lastNum));
}
//Security duties
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Security*Duties*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Duties is: %1', lastNum));
}
//Security privileges
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Security*Privileges*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Privileges is: %1', lastNum));
}
//Menus
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Menus*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Menus is: %1', lastNum));
}
//Services
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Services*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Services is: %1', lastNum));
}
//Code permissions
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Security*Code permissions*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Code permissions is: %1', lastNum));
}
//Workflow
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Workflow*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Workflows is: %1', lastNum));
}
//Parts
ttsBegin;
while select forUpdate syncLog
where syncLog.Processed == NoYes::No &&
syncLog.ItemPath like '*Parts*'
{
syncLog.BatchNum = lastNum + 1;
syncLog.update();
entered = true;
}
ttsCommit;
if (entered)
{
lastNum++;
entered = false;
info(strFmt('Batch number for Parts is: %1', lastNum));
}
}
The job above updates ‘SysVersionControlSynchronizeLog
’ and shows the batch numbers to process in an ‘Infolog’ window. So after running the job you can filter by batch id and click ‘Process’ button:
Problem 2
Cannot import elements that have same origin keys as existing elements in Dynamics AX 2012.
This problem occurs when some of the elements are renamed by other developer and by default the origin keys remain the same. So find the elements that cause the issues in your local repository, right click for properties, un-check the flag ‘Read only’:
Then open the ‘XPO’ file and delete all the lines with origin id:
This will modify only your local copies of the elements so don’t worry, the good versions are always on TFS. Next, run the process from ‘Synchronization log’ form again for the same type of elements that caused the problems before.
Problem 3
SQL server is throwing errors as it cannot execute any command on the particular table. This is caused because of change of indexes on the table which already had some data populated and validated by some old index. The solution to this is running the following job to delete records with duplicate keys:
static void KLFRemoveDuplicates(Args _args)
{
Set fieldSet = new set(Types::Integer);
DictIndex dictIndex = new DictIndex(
tablenum(KLFTestDuplicates),
indexnum(KLFTestDuplicates, AUniqueIdx));
int i;
;
if(dictIndex.numberOfFields())
{
for(i=1;i<=dictIndex.numberOfFields();i++)
{
fieldSet.add(dictIndex.field(i));
}
ReleaseUpdateDB::indexAllowDup(dictIndex);
ReleaseUpdateDB::deleteDuplicatesUsingIds(tablenum(KLFTestDuplicates), 0, fieldSet);
ReleaseUpdateDB::indexAllowNoDup(dictIndex);
}
info("done");
}
Be careful with this as it deletes records in your chosen table.
Happy DAXing.