D365 Business Central : AppVersion and DataVersion

In Microsoft Dynamics 365 Business Central, the ModuleInfo Data Type provides valuable information about the current extension, including the App Id, App Name, App Publisher, AppVersion, and DataVersion. This time we will explore the difference between AppVersion and DataVersion and how they can be used in extension development. These details are important in determining the context and versioning during installation, upgrade, and normal execution scenarios.

According to the documentation, AppVersion is the version of the specified application’s metadata. DataVersion is the version of the specified application’s data in the context of a given tenant. DataVersion indicates the last version that was installed or successfully upgraded to and will not match the application version if the tenant is in a data upgrade pending state.

Before we talk about AppVersion and DataVersion, let’s familiarize ourselves with the concept of ExecutionContext. It represents the context in which a session is running and can be categorized into three types: Normal, Install, and Upgrade. Although there is a fourth ExecutionContext called Uninstall, it is currently not used.

ExecutionContext

The value of AppVersion and DataVersion is determined by the ExecutionContext. Let’s examine their behavior in different execution scenarios:

  1. Normal ExecutionContext:
    • AppVersion: Refers to the current version being installed.
    • DataVersion: Refers to the current version being installed.
  2. Install ExecutionContext (Fresh Installation):
    • AppVersion: Refers to the new version being installed.
    • DataVersion: ‘0.0.0.0’ indicating no existing data (first-time installation).
  3. Install ExecutionContext (Reinstallation of the same version after Uninstall):
    • AppVersion: Refers to the current / previously installed version.
    • DataVersion: Refers to the current / previously installed version.
  4. Upgrade ExecutionContext:
    • AppVersion: Refers to the new version being installed.
    • DataVersion: Refers to the previously installed version.

Simulation

To better illustrate the behavior of AppVersion and DataVersion, let’s create a table to store these values.

table 60081 "App Version_TNG"
{
    Caption = 'App Version';
    DataClassification = CustomerContent;

    fields
    {
        field(1; "Entry No."; Integer)
        {
            Caption = 'Entry No.';
            DataClassification = CustomerContent;
            AutoIncrement = true;
        }
        field(2; "Execution Content"; Text[50])
        {
            Caption = 'Execution Content';
            DataClassification = CustomerContent;
        }
        field(3; "App Version"; Text[10])
        {
            Caption = 'App Version';
            DataClassification = CustomerContent;
        }
        field(4; "Data Version"; Text[10])
        {
            Caption = 'Data Version';
            DataClassification = CustomerContent;
        }
        field(5; "Comment"; Text[30])
        {
            Caption = 'Comment';
            DataClassification = CustomerContent;
        }
    }
    keys
    {
        key(PK; "Entry No.")
        {
            Clustered = true;
        }
    }

    procedure InsertVersion(CommentText: Text[30])
    var
        CurrModuleInfo: ModuleInfo;
    begin
        NAVApp.GetCurrentModuleInfo(CurrModuleInfo);
        Rec.Init();
        Rec."Execution Content" := Format(GetExecutionContext());
        Rec."App Version" := Format(CurrModuleInfo.AppVersion);
        Rec."Data Version" := Format(CurrModuleInfo.DataVersion);
        Rec.Comment := CommentText;
        Rec.Insert();
    end;
}

Next is to implement corresponding install and upgrade codeunit to record AppVersion and DataVersion.

codeunit 60081 "Install_TNG"
{
    Subtype = Install;

    trigger OnInstallAppPerCompany()
    var
        AppVersion: Record "App Version_TNG";
    begin
        AppVersion.InsertVersion('Install Codeunit');
    end;
}

codeunit 60082 "Upgrade_TNG"
{
    Subtype = Upgrade;

    trigger OnUpgradePerCompany()
    var
        AppVersion: Record "App Version_TNG";
    begin
        AppVersion.InsertVersion('Upgrade Codeunit');
    end;
}

Create a new page to display the table. Add an action on the page to manually insert AppVersion and DataVersion for simulating the Normal ExecutionContext.

page 60081 "App Version_TNG"
{
    ApplicationArea = All;
    Caption = 'App Version';
    PageType = List;
    SourceTable = "App Version_TNG";
    UsageCategory = History;
    Editable = false;

    layout
    {
        area(content)
        {
            repeater(General)
            {
                field("Entry No."; Rec."Entry No.")
                {
                    ApplicationArea = All;
                    ToolTip = 'Specifies the value of Entry No. field.';
                }
                field("Execution Content"; Rec."Execution Content")
                {
                    ApplicationArea = All;
                    ToolTip = 'Specifies the value of Execution Content field.';
                }
                field("App Version"; Rec."App Version")
                {
                    ApplicationArea = All;
                    ToolTip = 'Specifies the value of App Version field.';
                }
                field("Data Version"; Rec."Data Version")
                {
                    ApplicationArea = All;
                    ToolTip = 'Specifies the value of Data Version field.';
                }
            }
        }
    }

    actions
    {
        area(Processing)
        {
            action(InsertVersion)
            {
                ApplicationArea = All;
                Caption = 'Insert Version';
                ToolTip = 'Insert Version.';
                Image = Versions;

                trigger OnAction()
                var
                    AppVersion: Record "App Version_TNG";
                begin
                    AppVersion.InsertVersion('Page Action');
                end;
            }
        }
    }
}

Once all the preparations are complete, it’s time to start the testing. Let’s begin by installing our v1.0.0.0 app.

During the initial installation, we see that only the Install codeunit executes. The AppVersion reflects as 1.0.0.0, indicating the newly installed version.

Next, let’s proceed with upgrading our app to version 2.0.0.0 and publishing it.

On publishing v2.0.0.0, we notice that the AppVersion corresponds to the latest version, while the DataVersion refers to the previously installed version. This distinction shows the difference between AppVersion and DataVersion in Upgrade context.

Now let’s uninstall the app and reinstall it using Extension Management.

During the reinstallation of the v2.0.0.0 app, both the AppVersion and DataVersion display identical values. Additionally, we observe that only the Install codeunit is triggered during this process, as expected.

To further explore the behavior of AppVersion and DataVersion, let’s test the normal ExecutionContext by running the specified action on the page.

As anticipated, both the AppVersion and DataVersion are identical.

Use Cases

How we do we make use of the AppVersion and DataVersion ? We can use this information to perform a specific install or upgrade procedure. Here are a few example:

Data Migration: If there is a table schema change in a new version (e.g., v3.0.0.0), you can use the DataVersion to determine whether data migration should be performed. For example, perform data migration only when upgrading from a version earlier than v3.0.0.0.

codeunit 60082 "Upgrade_TNG"
{
    Subtype = Upgrade;

    trigger OnUpgradePerCompany()
    var
        AppVersion: Record "App Version_TNG";
        CurrModuleInfo: ModuleInfo;
    begin
        NAVApp.GetCurrentModuleInfo(CurrModuleInfo);
        PerformDataMigrationWhenComingFromV2(CurrModuleInfo);
        AppVersion.InsertVersion('Upgrade Codeunit');
    end;

    procedure PerformDataMigrationWhenComingFromV2(CurrModuleInfo: ModuleInfo)
    var
        AppVersion: Record "App Version_TNG";
    begin
        //Only do data migration when upgrading from version 2 and below
        if CurrModuleInfo.DataVersion.Major > 2 then
            exit;

        AppVersion.InsertVersion('Do Data Migration');
    end;
}

Controlled Upgrades: You can enforce a controlled upgrade path by checking the DataVersion. For instance, you can ensure that users cannot directly upgrade from version 1 to version 4 but instead require them to upgrade from version 1 to 3 before proceeding to version 4.

codeunit 60082 "Upgrade_TNG"
{
    Subtype = Upgrade;

    trigger OnUpgradePerCompany()
    var
        AppVersion: Record "App Version_TNG";
        CurrModuleInfo: ModuleInfo;
    begin
        NAVApp.GetCurrentModuleInfo(CurrModuleInfo);
        CheckUpgradeMustComeFromV3(CurrModuleInfo);
        AppVersion.InsertVersion('Upgrade Codeunit');
    end;

    procedure CheckUpgradeMustComeFromV3(CurrModuleInfo: ModuleInfo)
    begin
        if CurrModuleInfo.DataVersion.Major < 3 then
            Error('Please upgrade to version 3 first.');
    end;
}

That’s it. By understanding the behavior in different execution contexts, it will enable you to implement specific installation, upgrade, and data migration procedures.

GitHub link.

thatnavguy

Experienced NZ-based NAV Developer and Consultant with 15+ years of experience leading multiple IT projects, performing business analyst, developing, implementing, and upgrading Dynamics NAV and Business Central. Passionate to deliver solution that focuses on user-friendly interface while keeping high standard of compliance with the needs.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *