Pokud se v Dynamics AX změní ID tabulky nebo pole, dojde při synchronizaci ke ztrátě dat, protože je nejprve zahozena tabulka se starým ID (která obsahuje data) a následně je vytvořena nová tabulka se stejnou strukturou, ale jiným ID. To samé platí analogicky pro pole v tabulce.
Zabránit této ztrátě dat lze několikerým způsobem (třeba exportem a opětovným importem dat), ale jedno řešení je velmi prosté a bezbolestné. Uvědomte si, jak vypadá situace v AX po změně ID (třeba po instalaci vrstvy s jinými ID) ale ještě před synchronizací databáze:
- Data jsou stále v databázi
- Metadata tabulky v AOT obsahují nové ID
- Data v tabulce SqlDictionary obsahují staré ID
Není tedy třeba exportovat data nebo zaznamenávat stará ID, veškeré informace jsou stále v systému. Jen musíte zajistit, aby nedošlo k synchronizaci předčasně.
Porovnáním hodnot v AOT a v SqlDictionary lze identifikovat všechna změněná ID. A aktualizací SqlDictionary na novou hodnotu ID lze zabránit přegenerování databázových objektů během synchronizace.
Já k tomu používám několik scriptů, zde je nejjednodušší z nich:
Dictionary dictionary = new Dictionary(); SysDictTable dictTable; DictField dictField; TableId tableId; FieldId fieldId; SqlDictionary sqlDictionaryTable; SqlDictionary sqlDictionaryField; setPrefix("Aktualizace ID v datového slovníku"); tableId = dictionary.tableNext(0); ttsbegin; while (tableId) { dictTable = new SysDictTable(tableId); setPrefix(dictTable.name()); if (!dictTable.isSystemTable()) { //Najde tabulku v SqlDictionary dle jména v AOT, pokud se změnilo ID. //Prázné číslo pole říká, že jde o tabulku. select sqlDictionaryTable where sqlDictionaryTable.name == dictTable.name() && sqlDictionaryTable.fieldId == 0 && sqlDictionaryTable.tabId != dictTable.id(); if (sqlDictionaryTable) { //Aktualizuje ID tabulky SqlDictionary if (ReleaseUpdateDB::changeTableId( sqlDictionaryTable.tabId, dictTable.id(), dictTable.name())) { info(strFmt("ID tabulky změněno (%1 -> %2)", sqlDictionaryTable.tabId, dictTable.id())); } } fieldId = dictTable.fieldNext(0); //Pro všechna pole v tabulce while (fieldId) { dictField = dictTable.fieldObject(fieldId); if (dictField.isSql() && !dictField.isSystem()) { //Najde pole v SqlDictionary podle jména a porovná ID select sqlDictionaryField where sqlDictionaryField.tabId == dictTable.id() && sqlDictionaryField.name == dictField.name() && sqlDictionaryField.fieldId != 0 && sqlDictionaryField.fieldId != dictField.id(); if (sqlDictionaryField) { //Aktualizuje ID pole v SqlDictionary if (ReleaseUpdateDB::changeFieldId( dictTable.id(), sqlDictionaryField.fieldId, dictField.id(), dictTable.name(), dictField.name())) { info(strFmt("Pole %1 - ID změněno (%2 -> %3)", dictField.name(), sqlDictionaryField.fieldId, dictField.id())); } } } fieldId = dictTable.fieldNext(fieldId); } } tableId = dictionary.tableNext(tableId); } ttscommit;
Pokud mě paměť neklame, tento script funguje v AX4 – AX2012, ale Axapta 3.0 nemá v ReleaseUpdateDB metody changeTableId() a changeFieldId() a musíte si je tam naimplementovat sami.
Často se také zapomíná na to, že ID objektů se vyskytují také v uživatelských datech v databázi, příkladem může být ID tabulky, ke které je přiřazen nějaký dokument. Ignorování tohoto problému může narušit integritu databáze, kterou uživatel opět vnímá jako ztrátu dat. Jedním z možných řešení je použít můj DataReferenceSearcher – ten sice nalezené reference přímo neopravuje, ale můžete díky němu napsat potřebné scripty pro upgrade dat.