How to: Write Extension Upgrade Code
Extensions v1.0 are no longer supported. The text in the article applies to extensions v1.0. For information about writing extensions in AL, see the table below.
|For information about||See|
|Getting started writing extensions using the AL Language.||Getting Started|
|Converting extensions.||Converting Extensions V1 to Extensions V2|
|Writing extension install code for when an extension is installed for the first time, or for when an uninstalled extension is reinstalled.||Writing Extension Install Code|
|Making a newer version of an extension available.||Upgrading Extensions V2|
|Publishing, synchronizing, and installing the extension on the tenant.||Publishing and Installing an Extension v2.0|
The process of upgrading an extension includes uninstalling the current version of the extension and then installing a new version. If the extension modifies the schema of an existing table, or adds new tables, upgrade code will be required to handle the data that was automatically archived from the tables during uninstall. For more information about upgrading extensions, see Upgrading Extensions.
To write upgrade code for an extension
Create new codeunit.
This must be a codeunit of type Normal, not an Upgrade codeunit. For more information, see SubType Property.
Add upgrade functions to the codeunit. Make sure the function is declared as global so that the Local property is set to No.
- Add the OnNavAppUpgradePerDatabase() function if the extension contains cross-company tables.
- Add the OnNavAppUpgradePerCompany() function if the extension contains per-company tables.
Add the upgrade code to the appropriate function.
The upgrade code will depend on what you want to do with the archived data, plus additional factors such as the version of the archive data, whether the schema has changed, and whether the data can be modified by users. Determine which of the following upgrade methods is required for each new or modified table in your extension. For more information, see the next sections.
Restore Table Data
If the table has the same schema as the archive data and doesn’t require any special upgrade logic, then use the RESTOREARCHIVEDATA system function to copy the archived data to the table. The data that is copied may be for new tables added as part of the extension or for fields added to existing base tables.
The following code example restores the archived data for tables 50000 and 50001 on a company basis.
PROCEDURE OnNavAppUpgradePerCompany@1(); BEGIN NAVAPP.RESTOREARCHIVEDATA(50000); NAVAPP.RESTOREARCHIVEDATA(50001); END;
Delete Table Data
If you do not want to restore the archived data to the table, then use the DELETEARCHIVEDATA system function to delete the archived data.
The following code deletes the archived data for table 50002 on a company basis.
PROCEDURE OnNavAppUpgradePerCompany@1(); BEGIN NAVAPP.DELETEARCHIVEDATA(50002); END;
Upgrade Table Data
If the archived data is from a previous version and requires special upgrade logic, then use custom code to upgrade the records retrieved from the archive tables. For this scenario, you will use the following system functions:
Returns the extension version of archived data.
Returns a RecordRef for the identified table.
Restores the archived data to the database.
The following code upgrades records that are retrieves from the archived table for table 50003. The upgrade code that is run depends on the extension version number of the archived data.
VAR ArchiveRecRef@1000 : RecordRef; FldRef@1001 : FieldRef; DestRec@1002 : Record; Version@1003 : Text; PROCEDURE OnNavAppUpgradePerCompany@1(); BEGIN Version := NAVAPP.GETARCHIVEVERSION(); IF Version < '22.214.171.124' THEN BEGIN // The Version 1 to Version 2 upgrade code block. Copy over relevant old fields and initialize new fields using archive data. IF NAVAPP.GETARCHIVERECORDREF(50003, ArchiveRecRef) THEN BEGIN IF ArchiveRecRef.FINDSET(FALSE) THEN BEGIN DestRec.INIT; REPEAT FldRef := ArchiveRecRef.FIELD(50000); DestRec.Key := FldRef.VALUE; FldRef := ArchiveRecRef.FIELD(50001); DestRec.V1Field := FldRef.VALUE; DestRec.V2Field := 'NEW FIELD DEFAULT VALUE'; DestRec.INSERT; UNTIL ArchiveRecRef.NEXT = 0; END END END ELSE BEGIN // This is the reinstall (version 2 to version 2) block. // Because the schema is the same for all minor versions of the version 2 product, // you can copy the data in without changes. NAVAPP.RESTOREARCHIVEDATA(50003); END END;
The preceding sample code uses a strongly typed instance of the record for the destination. The other option is to use a RecordRef for both the source and destination. The sample code also assumes that any version 2 build of the extension uses the same table schema. If this is not true for your extension, you may have to use more specific version checks.
It is a best practice to use the
IF NAVAPP.GETARCHIVERECORDREF(50003, ArchiveRecRef) THEN BEGIN construct to catch any exception that is thrown, for example, if a non-valid table was passed.
Load Default or Starting Table Data
If the table should always be populated with starting or default data that was included in the package, then use the
NAVAPP.LOADPACKAGEDATA(TableNo) function to import the starting data to the table. If starting data is imported, then any archived data for the table will be deleted and not restored.
The following code example shows how to populate table 50004 with data.
PROCEDURE OnNavAppUpgradePerDatabase@1(); BEGIN NAVAPP.LOADPACKAGEDATA(50004); END;
Conditional Restore or Load Starting Table Data
In some situations, you may want to conditionally use different options. For example, say that if archive data exists for a new extension table, you want to restore that data. But if archive data doesn’t exist, you want to import starting data from the package. In this scenario you will want to check for the existence of an archive table using the NAVAPP.GETARCHIVERECORDREF(TableNo) function and if a RecordRef is returned then restore or upgrade the table. If a record isn’t returned, then use the function to import the starting data for the table.
PROCEDURE OnNavAppUpgradePerDatabase@1(); // Variables: // ArchiveRecRef RecordRef BEGIN IF NAVAPP.GETARCHIVERECORDREF(50001, ArchiveRecRef) THEN NAVAPP.RESTOREARCHIVEDATA(50001) ELSE NAVAPP.LOADPACKAGEDATA(50001); END;