Category Archives: AL Language

Partial Records

Partial Records are a new capability of Business Central introduced in Business Central 2020 release wave 2. It allows specifying fields that should be loaded when accessing SQL based data.

How does it work? Without using partial records, Business Central normally load all data from the record even if only one of them is required. The partial records, developer specify which fields are needed and the Business Central than load only that fields.

This is especially (based on Microsoft notes) important when Tableextensions are used. In that case, each Tableextension is stored as a separate table on the SQL server and, when the data from this table are loaded, SQL server joins the primary table, extension table (and other extension tables if exist) using join. Although the join is done using primary keys, the query is still much more performance-intensive than the query that accesses only one table.

Related Functions

There are four related functions:

  • [Ok := ] Record.SetLoadFields([Fields: Any,…])
    • Using the function SetLoadFields we specify a set of fields that server should load from the database. Fields have to be specified before the record is retrieved from the database (similar as SetAutoCalcFields()).
    • If the function is called more than once, only fields specified within the last call are loaded.
  • [Ok := ] Record.AddLoadFields([Fields: Any,…])
    • Similar function to SetLoadFields that has one big difference: if the function is called multiple times, the new call does not reset fields that were already set (even if they were set using SetLoadFields).
  • Ok := Record.AreFieldsLoaded(Fields: Any,…)
    • Checks whether the fields specified as parameters are already retrieved from the database.
  • [Ok := ] Record.LoadFields(Fields: Any,…)
    • This function allows loading data from the data source that was not loaded when the last partial record was retrieved (because the field was not specified using SetLoadFields nor AddLoadFields).

More specifically

In the first part, I wrote that only specified fields are loaded when the row is fetched from the database; however, if the fields were not loaded (using SetLoadFields / AddLoadFields) and system needs their value, they are automatically fetched using Just-In-Time (JIT) mechanism.

When JIT loading occurs, the system automatically loads data using the primary keys of the current record. This fetch might fail if the record was changed (modified / renamed / deleted) since the original data was retrieved. And that is the reason why the function LoadFields exists – when JIT loading occurs automatically, there is no way how to resolve if the loading fails. With LoadFields developers can implement explicit error handling for these fails.

Example

The example below is from Microsoft Docs and shows how to use SetLoadFields. As is mentioned on the Docs, in this case, the example is nine times faster than the same code without the Partial Records functionality.

 procedure ComputeArithmeticMean(): Decimal;
 var
     Item: Record Item;
     SumTotal: Decimal;
     Counter: Integer;
 begin
     Item.SetLoadFields(Item."Standard Cost");
     if Item.FindSet() then begin
         repeat
             SumTotal += Item."Standard Cost";
             Counter += 1;
         until Item.Next() = 0;
         exit(SumTotal / Counter);
     end;
 end

Microsoft example

As this is a very different approach to developing custom functionalities, I will definitely check the performance with some advance example codes in some of the next articles.

Interfaces in AL (part 3)

This is 3. part from the article series about Interfaces in AL Language. See the first one or the second one.

In previous parts, I have described the basics of Interfaces in AL Language (what is an interface, how to work with them in AL and started working on advanced example).

In the last part, we will continue with our advanced example and will add Enums to use the whole power of Interfaces.

Enums

Enums are described in a separate article.

Example

So, we use our Advanced example from part 2 as a starting point hence those who haven’t read the second part yet, check it first. Now, we create Enum specifically for this example.

 enum 50100 "TKA Inspection File Type"
 {
    Extensible = true;
    
    value(0; " ")
    {
        Caption = ' ';
    }
    value(5; "TKA BLB Type")
    {
        Caption = 'BLB Type';
    }
    value(10; "TKA XXS Type")
    {
        Caption = 'XXS Type';
    }
 }

We have an Enum that is similar to the original Option Data Type. But what is the advantage of Enums? With Enums, we can specify an implementation for every value in the object using property Implementation.

Firstly, the Enum must implement an interface(s). Then for every value, we can define a Codeunit object that implements the interface for the specific value. If the Enum implements an interface all values must have concrete specification through defined Codeunit. We can use property DefaultImplementation that will be used for values that do not have implementation specification defined directly.

 enum 50100 "TKA Inspection File Type" implements "TKA Inspection Format"
 {
    Extensible = true;
    DefaultImplementation = "TKA Inspection Format" = "TKA Default Format";
    
    value(0; " ")
    {
        Caption = ' ';
    }
    value(5; "TKA BLB Type")
    {
        Implementation = "TKA Inspection Format" = "TKA BLB Inspec. Format Mgnt.";
        Caption = 'BLB Type';
    }
    value(10; "TKA XXS Type")
    {
        Implementation = "TKA Inspection Format" = "TKA BLB Inspec. Format Mgnt.";
        Caption = 'XXS Type';
    }
 }

And finally, how to use this implementation. With the old approach, there will be an Options field on a page to allow users to choose a format that they are importing. Then in the code, there will be if/case statement which decides based on selected option value which Codeunit should be run. That means if we want to add a new format, we would have to change existing code (that could be in a different extension and impossible to change!).

However, with Enums, we have an Enum field on a page to allow users to choose the format (the Enum which is extensible from other extensions). Then in the code, we create an interface variable and assign the selected value of Enum. Then we call the requested methods using the interface object. That means no change is required when we need to add new supported format.

 codeunit 50100 "TKA Import Management"
 {
    procedure ImportFile(SelectedInspectionFileType: Enum "TKA Inspection File Type"; FilePath: Text)
    var
        InspectionFormat: Interface "TKA Inspection Format";
    begin
        InspectionFormat := SelectedInspectionFileType;
        InspectionFormat.LoadFile(FilePath);
    end;
 }

Interfaces in AL (part 2)

This is 2. part from the article series about Interfaces in AL Language. See the first one.

In the first part of this series, I have described what Interfaces are and showed the example of how they can be created. In this article, I will show a more advanced example of interfaces and how we can handle with these structure within Visual Studio Code.

Snippets

As usual, Microsoft added shortcut (snippet) to help with creating a basic layout of the interface object. The snippet is called “tinterface”.

I also already mentioned that I prefer (and strongly recommend) snippets from Visual Studio Code plugin “waldo’s CRS AL Language Extension” created by Waldo. However, in this case, there is nothing special from the snippet provided by Microsoft.


 // tinterface
 interface MyInterface
 {
    procedure MyProcedure();
 }

 // tinterfacewaldo
 interface IMyInterface
 {
    procedure MyProcedure();
 }

Example

Now we can look at the example. Our goal is to create functionality to import and process different types of machine inspection files. The files are of two types – BLB and XXS. Both have a completely different structure, and also data that provide are different, so they need to be processed differently.

In the old approach, we would have added if/case statement on every place where we need a different behaviour. So if we had to add another format later, we would have to change the code on these places once again. Not anymore with interfaces!

So we will create a new object of type Interface. This is the only type where we do not assign object ID.

 interface "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text);
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean;
 }

Now, we create Codeunit that implements our interface. To define which interface we are implementing, we use construction “Codeunit ID OBJECTNAME implement INTERFACE 1, INTERFACE 2, …”

We can see that the interface name is underlined and error messages are shown. As we use an interface, we have to define (implement…) every method from this interface(s). That can be done using “Quick Fixes” – a yellow light bulb or using a combination of Ctrl + .

 codeunit 50100 "TKA BLB Inspec. Format Mgnt." implements "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text)
    begin
    end;
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean
    begin
    end;
 }

Now we have a basic structure of the Codeunit that implements our interface. We can add some functionality and create another one in the same way with different functionality.

 codeunit 50100 "TKA BLB Inspec. Format Mgnt." implements "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text)
    begin
        ImportFileFromXML(FilePath);
    end;
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean
    begin
        DoSomething();
        DoTask1();
        exit(IsOk());
    end;
 }
 codeunit 50101 "TKA XXS Inspec. Format Mgnt." implements "TKA Inspection Format"
 {
    procedure LoadFile(FilePath: Text)
    var
        TKAFileManagement: Codeunit "TKA File Management";
    begin
        TKAFileManagement.DecryptFile(FilePath);
        while TKAFileManagement.HasNextLine() do  
            TKAFileManagement.ImportDecryptedLine(FilePath);
    end;
    procedure ProcessFile(InspectionFile: Record "TKA Inspection File"): Boolean
    begin
        DoSomethingDifferent();
        if HasError() then
            exit(false);

        DoFinalThing();
        exit(true);
    end;
 }

And finally, how to benefit from the interface. When we need to call any of the methods in the interface, we just need to define which implementation of the interface we are using.

 codeunit 50102 "TKA Some Settings"
 {
    procedure TKAInspectionFormatFactory(var TKAInspectionFile: Interface "TKA Inspection Format")
    begin
        // Get specific Implementation based on settings (for example)

        if Something() then
            TKAInspectionFile := TKABLBInspecFormatMgnt;
        else
            TKAInspectionFile := TKAXXSInspecFormatMgnt;
    end;
 }

 codeunit 50101 "TKA Some Mgnt. Object"
 {
    procedure ImportAndProcessFile()
    var
        TKAInspectionFile: Record "TKA Inspection File"

        TKAInspectionFormat: Interface "TKA Inspection Format";
        FilePath: Text;
    begin
        ...
        TKAInspectionFormatFactory(TKAInspectionFormat);
        if TKAInspectionFormat.LoadFile(FilePath) then begin
            ...
            TKAInspectionFormat.ProcessFile(TKAInspectionFile);
        end;
        ...
    end;
 }

Now we have a solution that requires only one change in method TKAInspectionFormatFactory if we need to add new supported file format.

In the next part, I will explain how to use Interface together with Enums – that is the most universal usage for an interface that is currently available in Business Central.

Interfaces in AL (part 1)

With Microsoft Dynamics 365 Business Central 2020 Wave 1 (respectively with Runtime 5.0) a new object type has been introduced – Interfaces. This term is well known from other programming languages and probably a lot of readers have already used them in some way.

The general definition of the interface object could be found anywhere on the Internet. For example, the definition on Wikipedia:

The term interface is used to define an abstract type that contains no data but defines behaviours as method signatures. A class having code and data for all the methods corresponding to that interface and declaring so is said to implement that interface.

An interface is thus a type definition; anywhere an object can be exchanged (for example, in a function or method call) the type of the object to be exchanged can be defined in terms of one of its implemented interfaces or base-classes rather than specifying the specific class. This approach means that any class that implements that interface can be used.

Usually a method defined in an interface contains no code and thus cannot itself be called; it must be implemented by non-abstract code to be run when it is invoked.

https://en.wikipedia.org/wiki/Interface_(computing)

Okay, but what does it mean in term of programming in AL language? The interface described in the quotation above is the new special object type “Interface” and the “non-abstract class” is, in our case, Codeunit object with a special construction that defines that the Codeunites implements an Interface.

 interface "TKA Our Interface Object"
 {
    procedure MyProcedureDefinedInTheInterfaceObject();
 }

 codeunit 50001 "TKA Our Codeunit Object" implements "TKA Our Interface Object"
 {
    procedure MyProcedureDefinedInTheInterfaceObject();
    begin
        // TODO Implementation of procedure MyProcedureDefinedInTheInterfaceObject
    end;
 }

In the next part of this article series, I will describe Interfaces on the more advanced example.

Enum Data Type

Enum is a new data type in the Business Central introduced with the very first version of Business Central. Since then many changes have been done to them and currently, they are usable instead of Option data type without any hesitation.

In compare to Option, Enums have more benefits and moreover, they can be shared across more fields without the need to specify them again and again for every field.

The main advantages are

  • Single specification of options across multiple fields.
  • Extensible (if allowed)
  • Better readability if more values are defined

Example

Enum looks like any other object in AL (see below). In the property section of the object, there is only one interesting property – Extensible. By default, the value is true; however, if we don’t want the enum extensible from another extension, we can change the value to false.

In the body part, we define values. For each value, we can define ID, Name and Caption. The major difference from Option Data Type is how we define ID. In option, we had to put as many commas as we needed to get the ID, in Enums, we just write the ID as a number.

 enum 50100 "TKA My Enum"
 {
    Extensible = true;
    
    value(0; " ")
    {
        Caption = ' ';
    }
    value(1; "TKA First Option")
    {
        Caption = 'First Option';
    }
    value(10; "TKA Second Option")
    {
        Caption = 'Second Option';
    }
 }

Usage

Usage of Enum is very similar to Record/Page/… variables. See the difference from Option declaration.

 table 50100 "TKA Table"
 {
    ...
    fields
    {
        ...
        field(100; "TKA Option Field"; Option)
        {
            OptionMember = " ","TKA First Option",,,,,,,,,"TKA Second Option";
            OptionCaption = ' ,First Option,,,,,,,,,Second Option';
        }
        field(101; "TKA Enum Field"; Enum "TKA My Enum")
        {
        }
        ...
    }
    ...
 }