Study guide

8.4 Installation & Upgrade Codeunits, Data Transfer & Archive

Microsoft Dynamics 365 Business Central Developer MB-820

Track your progress as you read

Finished sections get a check mark.

1 Chapter 1: Business Central Architecture & Platform Foundation

1.1 Business Central Architecture & Extensions Model 1.2 Customization Lifecycle: Cloud (SaaS) vs. On-Premises 1.3 Base Application, System Application & AppSource

2 Chapter 2: Development Environment & Configuration

2.1: Development Environment Setup: VS Code, AL: Go! Projects & Sandboxes 2.2: AL Configuration Files: app.json & launch.json Parameters 2.3: Multi-Root Workspaces & Project Dependencies

3 Chapter 3: Extension Lifecycle: Debugging, Deployment, Multilanguage & Maintenance

3.1 Debugging & Snapshot Debugging in Visual Studio Code 3.2 Extension Deployment & Schema Synchronization Modes 3.3 Multilanguage Development with XLIFF & Translation Files 3.4 Extension Maintenance, Deprecation & Breaking Changes

4 Chapter 4: Tables, Table Extensions & Data Modeling

4.1: Building Tables, Fields, Keys & Data Types 4.2: Table Extensions & Table Modifications 4.3: FlowFields, FlowFilters & Table Relations 4.4: Enums, Enum Extensions & Extensible Types

5 Chapter 5: Pages, Page Extensions & Role Centers

5.1: Page Types, Anatomy & Control Structure 5.2: Building Card, List & Document Pages 5.3: Page Extensions & Page Customizations 5.4: Role Centers, Headlines & Cue Data Modeling

6 Chapter 6: Reports & Document Generation

6.1 Report Architecture, DataItems & Dataset Design 6.2 Report Layouts: RDLC, Word, Excel & Multilanguage 6.3 Document Reports & Request Page Programming 6.4 Processing-Only Reports & Report Substitutions

7 Chapter 7: XMLports & Data Exchange

7.1 XMLport Architecture, Formats & Properties 7.2 XMLport Node Hierarchy, Triggers & Stream Manipulation

8 Chapter 8: Codeunits, Events, Triggers & Interfaces

8.1 Codeunit Types, Execution & SingleInstance Codeunits 8.2 Events, Publishers, Subscribers & Event Isolation 8.3 Interfaces, Polymorphism & Dynamic Codeunit Invocation 8.4 Installation & Upgrade Codeunits, Data Transfer & Archive

9 Chapter 9: Entitlements, Permissions & Queries

9.1 Permission Sets & Permission Set Extensions 9.2 Inherent Permissions, Inherent Entitlements & Security Scoping 9.3 Query Objects: Joins, Aggregation & Query as Report Data Source

10 Chapter 10: UI Customization, User Assistance & Onboarding

10.1 Profile Objects & Custom Page Views in AL 10.2 User Assistance: ToolTips, Teaching Tips & In-App Tours 10.3 Assisted Setup, Guided Experience & Onboarding Checklists

11 Chapter 11: Business Central Design Patterns & Data Process Model

11.1 Functional Table Types: Master Data, Supplemental & Setup Patterns 11.2 Document Standards: Header, Line, History & Journal Architectures 11.3 Data Process Model & Posting Routine Architecture

12 Chapter 12: AL Language Fundamentals, Data Types & Variables

12.1 AL Syntax, Variable Scope, Primitive & Complex Data Types 12.2 Expressions, Control Flow Statements & Built-in Functions 12.3 Access Modifiers (Public, Internal, Local) & Code Scoping

13 Chapter 13: Data Manipulation, Streams & Error Handling

13.1 Record Operations: Get, FindSet, ModifyAll & Filter Functions 13.2 Streams, File Management & TempBlob 13.3 Error Handling: ErrorInfo, TryFunctions & Collectible Errors

14 Chapter 14: Testing, Test Toolkit & Automation

14.1 Testing Architecture & the Test Toolkit 14.2 Authoring Test Codeunits & UI Handler Functions

15 Chapter 15: Telemetry, Performance Optimization & Analyzers

15.1 Telemetry Configuration & Azure Application Insights 15.2 Custom Telemetry Signals with Session.LogMessage & KQL 15.3 Performance Profiler, SetLoadFields & Query Optimization 15.4 AL Analyzers: CodeCop, AppSourceCop, UICop & PTECop

16 Chapter 16: REST Web Services & JSON in AL

16.1 HttpClient, HttpRequestMessage & REST API Consumption 16.2 JSON Processing: JsonObject, JsonArray, JsonValue & JsonToken

17 Chapter 17: API Pages, OData, Read Scale-Out & OAuth

17.1 API Pages (PageType=API), OData Web Services & REST Endpoints 17.2 Bound and Unbound OData Actions with [ServiceEnabled] 17.3 Read Scale-Out Architecture & DataAccessIntent Configuration 17.4 Microsoft Entra ID App Registrations & OAuth 2.0 Integration

8.4 Installation & Upgrade Codeunits, Data Transfer & Archive

Key Takeaways Explain This Topic AI

Installation codeunits (Subtype = Install) run during extension installation or synchronization, exposing OnInstallAppPerCompany and OnInstallAppPerDatabase triggers for initial configuration.

Upgrade codeunits (Subtype = Upgrade) manage data transformations across versions via a strict, sequential six-stage trigger execution pipeline.

The ModuleInfo record retrieved via NavApp.GetCurrentModuleInfo provides DataVersion and AppVersion properties to conditionally gate upgrade procedures based on previous database state.

The DataTransfer object executes set-based, bulk SQL operations (CopyFields, CopyRows) directly on the database engine, bypassing slow row-by-row AL loops and preventing SaaS timeouts.

Archive management methods (NavApp.RestoreArchiveData and NavApp.DeleteArchiveData) govern data recovery and cleanup when refactoring or deprecating obsolete tables.

Last updated: August 2026

8.4 Installation & Upgrade Codeunits, Data Transfer & Archive

When deploying, reinstalling, or updating extensions in Business Central environments, developers must ensure seamless database initialization and schema migration. AL provides specialized codeunit subtypes to handle these lifecycles: Install Codeunits ( Subtype = Install ) for first-time tenant setup, and Upgrade Codeunits ( Subtype = Upgrade ) for managing multi-version data transformations. Understanding the exact execution order, version evaluation APIs, and the modern DataTransfer set-based migration API is heavily tested on the MB-820 exam.

1. Installation Codeunits ( Subtype = Install )

An Install codeunit executes automatically whenever an extension is installed or synchronized in a tenant. It contains system triggers that initialize configuration tables, generate number series, and register assisted setup wizards. codeunit 50140 "App Installation Handler"

  • {
  • Subtype = Install;
  • trigger OnInstallAppPerCompany()
  • var
  • AppSetup: Record "Custom App Setup";
  • begin
  • // Executed for every individual company in the tenant
  • if not AppSetup.Get() then begin
  • AppSetup.Init();
  • AppSetup."Default Batch Name" := 'GENERAL';
  • AppSetup."Enable Telemetry" := true;
  • AppSetup.Insert(true);
  • end;
  • InitializeDefaultNumberSeries();
  • end;
  • trigger OnInstallAppPerDatabase()
  • begin
  • // Executed once across the entire tenant database
  • // Used for cross-company tables (DataPerCompany = false) or global database setup
  • end;
  • local procedure InitializeDefaultNumberSeries()
  • var
  • NoSeries: Record "No. Series";
  • NoSeriesLine: Record "No. Series Line";
  • begin
  • if not NoSeries.Get('CUST-PROMO') then begin
  • NoSeries.Init();
  • NoSeries.Code := 'CUST-PROMO';
  • NoSeries.Description := 'Customer Promotion Series';
  • NoSeries."Default Nos." := true;
  • NoSeries.Insert();
  • NoSeriesLine.Init();
  • NoSeriesLine."Series Code" := NoSeries.Code;
  • NoSeriesLine."Line No." := 10000;
  • NoSeriesLine."Starting No." := 'PRM0001';
  • NoSeriesLine."Ending No." := 'PRM9999';
  • NoSeriesLine.Insert();
  • end;
  • end;
  • }
  • Triggers and Idempotency Best Practices

OnInstallAppPerCompany() : Runs once for each individual company in the Business Central tenant database. It should populate company-specific setup tables and master records.

OnInstallAppPerDatabase() : Runs once across the entire tenant database, regardless of how many companies exist. Ideal for tables configured with DataPerCompany = false .

Idempotency Rule : Install triggers can execute multiple times (e.g., if the extension is uninstalled and reinstalled, or when a new company is created). Code must be strictly idempotent —always verify if records exist ( if not Setup.Get() then ... ) before inserting to prevent primary key collisions.

Loading diagram... Complete Upgrade Codeunit Execution Pipeline (6 Sequential Stages)

2. Upgrade Codeunits ( Subtype = Upgrade )

When a newer version of an extension is deployed over an existing version, the platform invokes Upgrade codeunits. Upgrade codeunits run through a strict six-stage execution pipeline across database and company contexts.

The Six Upgrade Triggers in Execution Sequence

Order Trigger Name Context Purpose

1 OnCheckPreconditionsPerDatabase() Database Verify global database prerequisites before any upgrade step begins.

2 OnCheckPreconditionsPerCompany() Company Verify company-specific prerequisites; fail fast if required historical data is invalid.

3 OnUpgradePerDatabase() Database Execute data migrations on shared cross-company tables ( DataPerCompany = false ).

4 OnUpgradePerCompany() Company Execute primary data migrations, table transformations, and field mappings per company.

5 OnValidateUpgradePerDatabase() Database Validate data integrity and record counts across the global database post-upgrade.

6 OnValidateUpgradePerCompany() Company Validate that company-level records and migrated fields meet business rules.

Failure Semantics & Rollback

If an unhandled error occurs during any trigger in the six-step sequence:

The entire upgrade process terminates immediately.

All database modifications made across the upgrade transaction are completely rolled back.

The tenant remains on the existing extension version without data corruption.

Version-Checking with NavApp.GetCurrentModuleInfo

To ensure upgrade routines only run when updating from specific prior builds, developers inspect DataVersion (the version of the data currently stored in the database) against AppVersion (the version of the code package being installed). codeunit 50145 "App Upgrade Handler"

  • {
  • Subtype = Upgrade;
  • trigger OnUpgradePerCompany()
  • var
  • AppInfo: ModuleInfo;
  • begin
  • NavApp.GetCurrentModuleInfo(AppInfo);
  • // Gating upgrade logic: Run only if upgrading from Version 1.x to 2.x
  • if AppInfo.DataVersion.Major < 2 then
  • MigrateCustomerLegacyCodes();
  • end;
  • local procedure MigrateCustomerLegacyCodes()
  • begin
  • // Migration logic
  • end;
  • }

Exam Watchout — DataVersion vs. AppVersion:

AppInfo.DataVersion : Represents the version of the data currently persisted in the tenant database. For a fresh install on a clean tenant, DataVersion = Version.Create(0, 0, 0, 0) .

AppInfo.AppVersion : Represents the version of the incoming code extension package defined in app.json .

3. High-Performance Bulk Migration: The DataTransfer Object

Historically, migrating data between tables during an upgrade required iterating through records with FindSet() and updating them row-by-row with Modify() . In cloud tenants with millions of rows, this generated massive transaction logs and timed out. AL introduced the DataTransfer object, which compiles directly into set-based SQL statements ( INSERT INTO ... SELECT and UPDATE ... FROM ), executing directly in the database engine. codeunit 50150 "Fast Data Upgrade"

  • {
  • Subtype = Upgrade;
  • trigger OnUpgradePerCompany()
  • var
  • AppInfo: ModuleInfo;
  • begin
  • NavApp.GetCurrentModuleInfo(AppInfo);
  • if AppInfo.DataVersion.Major < 2 then begin
  • CopyCustomerFieldsBulk();
  • MigrateLegacyTableRowsBulk();
  • end;
  • end;
  • local procedure CopyCustomerFieldsBulk()
  • var
  • Customer: Record Customer;
  • DataXfer: DataTransfer;
  • begin
  • // Set-based field copy within the same table
  • DataXfer.SetTables(Database::Customer, Database::Customer);
  • DataXfer.AddFieldValue(Customer.FieldNo("Legacy Loyalty Code"), Customer.FieldNo("Loyalty ID"));
  • DataXfer.AddSourceFilter(Customer.FieldNo("Legacy Loyalty Code"), '<>%1', '');
  • DataXfer.CopyFields(); // Emits a single direct SQL UPDATE statement
  • end;
  • local procedure MigrateLegacyTableRowsBulk()
  • var
  • LegacyStaging: Record "Legacy Staging Table";
  • NewArchive: Record "New Customer Archive";
  • DataXfer: DataTransfer;
  • begin
  • // Bulk copy rows from one table into a new table
  • DataXfer.SetTables(Database::"Legacy Staging Table", Database::"New Customer Archive");
  • DataXfer.AddFieldValue(LegacyStaging.FieldNo("Entry No."), NewArchive.FieldNo("Entry No."));
  • DataXfer.AddFieldValue(LegacyStaging.FieldNo("Customer Code"), NewArchive.FieldNo("Customer No."));
  • DataXfer.AddFieldValue(LegacyStaging.FieldNo("Amount"), NewArchive.FieldNo("Total Balance"));
  • DataXfer.AddConstantValue(Today(), NewArchive.FieldNo("Migrated Date"));
  • DataXfer.CopyRows(); // Emits a single direct SQL INSERT INTO ... SELECT statement
  • end;
  • }
  • DataTransfer Methods & Rules

SetTables(SourceTableId, TargetTableId) : Specifies source and destination tables.

AddFieldValue(SourceFieldNo, TargetFieldNo) : Maps source columns to target columns.

AddConstantValue(Value, TargetFieldNo) : Populates target fields with a fixed constant value.

AddJoin(SourceFieldNo, TargetFieldNo) : Establishes join conditions between distinct source and target tables when updating fields.

AddSourceFilter(SourceFieldNo, FilterString) : Limits the SQL query to a filtered subset of rows.

CopyFields() : Executes a set-based SQL UPDATE .

CopyRows() : Executes a set-based SQL INSERT INTO ... SELECT .

Platform Restrictions : DataTransfer can only be executed within Upgrade codeunits ( Subtype = Upgrade ). It bypasses table triggers and validation for maximum performance and cannot target system tables.

4. Data Archive Management & Table Deprecation

When a table or table extension is marked ObsoleteState = Removed or during breaking schema changes where fields are refactored into new extensions, the platform safeguards data using table archives.

Data Archive Lifecycle APIs

NavApp.RestoreArchiveData(Database::TableName) : Restores archived data from a previous version into the target table during an upgrade.

NavApp.DeleteArchiveData(Database::TableName) : Permanently purges backed-up archive data once validation has succeeded, freeing database storage.

Comparison of Data Migration Approaches

Migration Approach Execution Mechanism Performance on 1M+ Records Trigger Execution Permitted Context

DataTransfer Object Set-based SQL ( UPDATE / INSERT SELECT ) Instantaneous (seconds) No (Bypasses triggers) Subtype = Upgrade only

Record Looping ( FindSet + Modify ) Row-by-row AL iteration Extremely slow (minutes/hours; timeouts) Optional ( Modify(true) ) Anywhere in AL

  • ModifyAll() Parameterized SQL UPDATE Fast No Anywhere in AL
  • XMLport Export/Import File-based streaming Slow (I/O intensive) Optional Normal / Batch Jobs