Monday, 10 September 2012

Installing Feature pack on AX 2012 CU3

I recently had to install the Feature pack on CU3 environment and I thought I should share my experience.

Note: I won’t go through every single detail. Just the database and models.

 

  • Install the Feature pack for the Database using the full Feature pack disc

This will warn you that you have a new model and it may override

clip_image001

  • Install CU3 and this should import a model "Extensions update" model
  • Install your custom model - merge whatever you need to merge
  • Start your AOS - make sure your service and client are both on the CU3 build
  • Run the client and you should see "The model store has been modified" pop up

Select "Start the software update checklist" since this is a minor update

clip_image002

  • The software update checklist will start

Follow the check list - I did my merge already before so I didn’t run the "Detect code upgrade conflicts". I marked it as compete. You could do yours here.

clip_image003

  • When you get to the "Synchronize database" it may fail. It failed for me because of ID conflicts.

clip_image004

I did a bit of digging and found the application id and the SQL dictionary id was different.

clip_image005

It renumbered the field DueDateLimitGroupId_ES (Foundation KB2709934 which is CU3) from 60001 to 60003.
Then the new field "DefaultPaymTerm_PSN" (Extensions model) took 60001. That is why it tries to run an update of the field and failed.

Only thing I can point it at is that when I installed the feature pack. It removed CU3, then installed the feature pack. Which took the first free ID. Then when I installed CU3, it took the next free ID.

  • To fix this issue I had to run a clean up job to update the SQL dictionary with the new ID. If there is a name match.

Thanks to this blog http://dev.goshoom.net/en/2011/11/id-change/

I made a small change to skip views as that didn’t work.

static void UpdateDataDictionaryID(Args _args)
{
Dictionary dictionary = new Dictionary();
SysDictTable dictTable;
DictField dictField;
TableId tableId;
FieldId fieldId;
SqlDictionary sqlDictionaryTable;
SqlDictionary sqlDictionaryField;   setPrefix("Update of data dictionary IDs");
tableId = dictionary.tableNext(0);
ttsbegin;   while (tableId)
{
dictTable = new SysDictTable(tableId);
setPrefix(dictTable.name());   if (!dictTable.isSystemTable() && !dictTable.isView())
{
//Finds table in SqlDictionary by name in AOT, if ID was changed.
//Empty field ID represents a table.
select sqlDictionaryTable
where sqlDictionaryTable.name == dictTable.name()
&& sqlDictionaryTable.fieldId == 0
&& sqlDictionaryTable.tabId != dictTable.id();   if (sqlDictionaryTable)
{
//Updates table ID in SqlDictionary
if (ReleaseUpdateDB::changeTableId(
sqlDictionaryTable.tabId,
dictTable.id(),
dictTable.name()))
{
info(strFmt("Table ID changed (%1 -> %2)", sqlDictionaryTable.tabId, dictTable.id()));
}
}   fieldId = dictTable.fieldNext(0);   //For all fields in table
while (fieldId)
{
dictField = dictTable.fieldObject(fieldId);   if (dictField.isSql() && !dictField.isSystem())
{
//Finds fields in SqlDictionary by name and compares IDs
select sqlDictionaryField
where sqlDictionaryField.tabId == dictTable.id()
&& sqlDictionaryField.name == dictField.name()
&& sqlDictionaryField.fieldId != 0
&& sqlDictionaryField.fieldId != dictField.id();   if (sqlDictionaryField)
{
//Updates field ID in SqlDictionary
if (ReleaseUpdateDB::changeFieldId(
dictTable.id(),
sqlDictionaryField.fieldId,
dictField.id(),
dictTable.name(),
dictField.name()))
{
info(strFmt("Field %1 - ID changed (%2 -> %3)",
dictField.name(),
sqlDictionaryField.fieldId,
dictField.id()));
}
}
}
fieldId = dictTable.fieldNext(fieldId);
}
}
tableId = dictionary.tableNext(tableId);
}
ttscommit;   info("Done");
}


  • To deal with the views as that will error out with the sync (very weird errors but the Event viewer errors are more helpful), I wrote a job to give me an info log to drop all the views. If you are concerned about this - you can just run the sync and get a list of views it fails on and just delete them one at a time.

Copy the infolog and run it in SQL.






static void ShowViewDataDictionaryID(Args _args)
{
Dictionary dictionary = new Dictionary();
SysDictTable dictTable;
DictField dictField;
TableId tableId;
FieldId fieldId;
SqlDictionary sqlDictionaryTable;
SqlDictionary sqlDictionaryField;   tableId = dictionary.tableNext(0);   while (tableId)
{
dictTable = new SysDictTable(tableId);
setPrefix(dictTable.name());   if (!dictTable.isSystemTable() && dictTable.isView())
{
info(strFmt("DROP VIEW %1",dictTable.name()));   }
tableId = dictionary.tableNext(tableId);
}   info("Done");
}



  • Continue the sync. This time it should work fine. You may have to do it twice to get it to work smooth. This is because of the views deleted may have dependencies.

  • Complete the remaining checklist tasks

Good to see someone had a similar issue recently.

http://sashanazarov.blogspot.com/2012/09/id-change-in-dynamics-ax-data-dictionary.html

 

Final thought.

We should go with the Feature pack with all new clients. Microsoft has already put a strong statement out on partner source on why we should adopt it.

The Microsoft Dynamics AX 2012 Feature Pack—Why You Should Adopt it Moving Forward

"When Microsoft Dynamics AX 2012 R2 is released in H1 Calendar Year 2013 it will automatically install the remaining Microsoft provided country localizations (Asia, South America, Eastern Europe, etc.); so that we maintain the single consistent Microsoft Dynamics AX 2012 code base."

From this statement, it looks like Microsoft will release a single code base and their won't be a choice in the future. Everything will be installed, including the industry solutions (ie. Feature pack).

No comments: