Business central blog
Subscribe to blog and get news about new posts.

Let's break down the Business Central

I think Friday is the perfect day to break Business Central! Very dangerous, do not repeat this on Production!

Destroy the server instance

Let's start by breaking the server instance. After executing this code, the server instance will break for all users. Cloud Business Central will recover after some time, but I would not recommend using this on production. Yes, this code doesn't do anything useful, quite the opposite actually :)

page 81008 "DET Destroy Instance"
{
  PageType = List;
  ApplicationArea = All;
  UsageCategory = Lists;
  SourceTable = "DET Some Table";
  SourceTableTemporary = true;
  Caption = 'Destroy Instance';

  layout
  {
    area(Content)
    {
      repeater(Repgroup)
      {
        field("Entry No."; Rec."Entry No.")
        {
          ToolTip = 'Specifies the value of the Entry No. field.';
        }
        field(Description; Rec.Description)
        {
          ToolTip = 'Specifies the value of the Description field.';
        }
      }
    }
  }
  actions
  {
    area(Processing)
    {
      action("DET DestroyInstance")
      {
        ApplicationArea = All;
        Caption = 'Destroy Instance';
        ToolTip = 'Destroy Instance';
        Image = DeleteExpiredComponents;
        trigger OnAction()
        begin
          Execute();
        end;
      }
    }
    area(Promoted)
    {
      actionref("DET DestroyInstance_promoted"; "DET DestroyInstance")
      {

      }
    }
  }

  procedure Execute()
  var
    SessionIndex, StartIndex, EndIndex : Integer;
    ListOfParts: List of [Integer];
    i: Integer;
    ParallelSessionCount: Integer;
  begin
    ParallelSessionCount := 6;
    StartIndex := 1;

    ListOfParts.Add(111);
    ListOfParts.Add(112);
    ListOfParts.Add(113);
    ListOfParts.Add(114);
    ListOfParts.Add(115);
    ListOfParts.Add(112);

    Rec.Init();
    Rec."Entry No." := 1;
    Rec.Insert();

    for i := 1 to ParallelSessionCount do begin

      EndIndex := StartIndex + ListOfParts.Get(i) - 1;

      Rec."Start Index" := StartIndex;
      Rec."End Index" := EndIndex;

      if i > 1 then
        StartSession(SessionIndex, Codeunit::"DET Insert Record Range", CompanyName(), Rec);

      StartIndex := EndIndex + 1;
    end;

  end;
}

codeunit 81002 "DET Insert Record Range"
{
  TableNo = "DET Some Table";
  trigger OnRun()
  var
    TempRecRef: RecordRef;
    FieldRef: FieldRef;
    StartIndex, EndIndex : Integer;
    i, j : Integer;
  begin
    Sleep(500);
    TempRecRef.GetTable(Rec);

    StartIndex := Rec."Start Index";
    EndIndex := Rec."End Index";

    for i := StartIndex to EndIndex do begin
      TempRecRef.Init();
      for j := 1 to 2 do begin
        FieldRef := TempRecRef.Field(j);
        FieldRef.Value(i);
      end;
      TempRecRef.Insert();
    end;
  end;
}

table 81003 "DET Some Table"
{
  DataClassification = CustomerContent;

  fields
  {
    field(1; "Entry No."; Integer)
    {
      DataClassification = CustomerContent;
      Caption = 'Entry No.';
    }
    field(2; Description; Text[100])
    {
      DataClassification = CustomerContent;
      Caption = 'Description';
    }
    field(3; "Start Index"; Integer)
    {
      DataClassification = CustomerContent;
      Caption = 'Start Index';
    }
    field(4; "End Index"; Integer)
    {
      DataClassification = CustomerContent;
      Caption = 'End Index';
    }
  }

  keys
  {
    key(PK; "Entry No.")
    {
      Clustered = true;
    }
  }

}

Incorrect table reference

Let's fix this code to make it do something useful. We will use an array of Record to populate each instance of the variable with different ranges in current session. Just fill data into temporary table, nothing special.

procedure Execute()
var
  TempSomeTable: array[6] of Record "DET Some Table" temporary;
  StartIndex, EndIndex : Integer;
  ListOfParts: List of [Integer];
  i: Integer;
  Parts: Integer;
  ResultTxt: TextBuilder;
begin
  Parts := 6;
  StartIndex := 1;

  ListOfParts.Add(100);
  ListOfParts.Add(100);
  ListOfParts.Add(100);
  ListOfParts.Add(100);
  ListOfParts.Add(100);
  ListOfParts.Add(100);

  for i := 1 to Parts do begin

    EndIndex := StartIndex + ListOfParts.Get(i) - 1;

    TempSomeTable[i]."Start Index" := StartIndex;
    TempSomeTable[i]."End Index" := EndIndex;

    Codeunit.Run(Codeunit::"DET Insert Record Range", TempSomeTable[i]);

    StartIndex := EndIndex + 1;

    ResultTxt.AppendLine(StrSubstNo('Count is %1 for table %2', TempSomeTable[i].Count(), i));
  end;

  Message('The numbers are going up?!\%1', ResultTxt.ToText());
end;
But what happened? I expected to see 100 entries in each of TempSomeTable[i], but instead, I encountered a strange situation where each subsequent element of the TempSomeTable array includes the previous records. Strange, isn't it?
Natalie Karolak pointed out that this is a documented behavior. Therefore, it's not a bug, but a feature! Unfortunately, it's still unclear what problem this feature solves, but it is known for sure that it complicates working with the array of the temporary table.

Unexpected commit

What do you think will be the result of executing such code?

pageextension 81000 "DET Customer List" extends "Customer List"
{
    trigger OnOpenPage()
    var
        Vendor: Record Vendor;
    begin
        Rec.Init();
        Rec."No." := 'test';
        Rec.Insert();

        Vendor.Init();
        Vendor."No." := 'test';
        Vendor.Insert();

        Error('Stop!');
    end;
}

Guessed it? You will end up creating Customer and Vendor with the number 'test'! Errors do not roll back transactions on the OnOpenPage trigger. Pretty cool, right?

Simple action shortcut key

What happens if we assign such a ShortCutKey for an Action? Spoiler - it will be very bad -)

ShortcutKey = 'Shift+k';

Summary

I think it's enough breaking Business Central for today, time to get ready for the weekend!
May 10, 2024