Domain 4 β€” Module 1 of 6 17%
11 of 28 overall
Domain 4: Develop and Test Code Free ⏱ ~16 min read

X++ Fundamentals: Syntax & CRUD

Learn X++ structured programming, CRUD data operations, global functions, and best practice rules.

X++ Fundamentals: Syntax & CRUD

Welcome to Domain 4 β€” the heart of MB-500. X++ powers every customisation in D365 Finance and Operations.

Simple explanation

Think of X++ as C# with a built-in database.

X++ has SQL-like statements baked into the language β€” select, while select, insert. It also has built-in transactions (ttsBegin/ttsCommit). If you know C# or Java, X++ feels familiar with a few unique twists like the container type and 1-based indexing.

Variables and Data Types

TypeDescriptionExample
int / int64Integerint count = 10;
realDecimalreal price = 29.99;
strStringstr name = "Contoso";
booleanTrue/falseboolean active = true;
date / utcdatetimeDate valuesdate today = systemDateGet();
containerMixed-type collectioncontainer c = [1, "hello", true];

πŸŽ“ Sophie’s first day: β€œCarl said the biggest surprise is the container type β€” like a tuple holding any mix. Indexing starts at 1, not 0!”

Containers

container data = [1001, "Contoso", 29.99];
int id   = conPeek(data, 1);    // 1001 (1-based!)
str name = conPeek(data, 2);    // "Contoso"
int size = conLen(data);         // 3
container updated = conIns(data, 2, "NYC"); // new container
Exam tip: Container indexing starts at 1

conPeek(c, 0) causes a runtime error. Containers are 1-based. The exam tests this.

Control Flow

if (custTable.AccountNum == "1001")
    info("Found");
else
    error("Not found");

switch (salesLine.SalesStatus)
{
    case SalesStatus::Backorder: info("Back"); break;
    case SalesStatus::Delivered: info("Done"); break;
}

for (int i = 1; i <= 10; i++)
    info(strFmt("Item %1", i));

πŸ—οΈ Vik: β€œ90% of the time you loop with while select, not plain for/while.”

Reading Data β€” select

CustTable custTable;
select firstOnly custTable where custTable.AccountNum == "1001";
if (custTable)
    info(strFmt("Customer: %1", custTable.Name));

while select

CustTable custTable;
while select custTable where custTable.Blocked == CustVendorBlocked::No
{
    info(strFmt("%1 - %2", custTable.AccountNum, custTable.Name));
}

Joins and aggregates

CustTable custTable;
CustTrans custTrans;
while select custTable
    join custTrans where custTrans.AccountNum == custTable.AccountNum
{
    info(strFmt("%1: %2", custTable.Name, custTrans.Voucher));
}

select count(RecId) from custTable where custTable.Blocked == CustVendorBlocked::No;
select Keywords
KeywordPurpose
firstOnlyReturn one record
order bySort results
group by / count / sumAggregates
join / outer joinTable joins
exists join / notexists joinExistence filter
forUpdateLock for modification

Writing Data β€” Insert, Update, Delete

// INSERT
CustTable custTable;
ttsBegin;
    custTable.AccountNum = "9001";
    custTable.Name = "New Customer";
    custTable.insert();
ttsCommit;

// UPDATE (forUpdate required!)
ttsBegin;
    select forUpdate custTable where custTable.AccountNum == "9001";
    if (custTable) { custTable.Name = "Updated"; custTable.update(); }
ttsCommit;

// DELETE
ttsBegin;
    select forUpdate custTable where custTable.AccountNum == "9001";
    if (custTable) { custTable.delete(); }
ttsCommit;

Key patterns: ttsBegin/ttsCommit for transactions. forUpdate locks the record. Always check if (buffer) before update/delete.

Exam tip: tts nesting

Transaction scopes nest. Only the outermost ttsCommit commits. Any exception rolls back everything.

Global Functions

FunctionPurpose
strFmt("Hello %1", name)String formatting
conPeek(c, 1) / conLen(c)Container ops
systemDateGet()Current date
info() / warning() / error()Infolog messages
classStr() / tableStr()Compile-time names

Best Practice Rules

The compiler includes a BP checker β€” like ESLint for X++.

RuleCatchesFix
Missing labelHardcoded UI stringsUse @SYS labels
Select without whereUnbounded queriesAdd where clause
Unused variableDeclared not usedRemove it

πŸ‘¨β€πŸ’» Elena: β€œUse strFmt for all formatting, not concatenation. Cleaner, faster, translatable.”

Question

select firstOnly vs while select?

Click or press Enter to reveal answer

Answer

firstOnly gets one record. while select iterates all matches.

Click to flip back

Question

What does ttsBegin/ttsCommit do?

Click or press Enter to reveal answer

Answer

Transaction scope. All modifications atomic β€” all succeed or roll back.

Click to flip back

Question

What keyword before update()/delete()?

Click or press Enter to reveal answer

Answer

forUpdate β€” locks the record. Without it, runtime error.

Click to flip back

Knowledge Check

Correct X++ to fetch one customer?

Knowledge Check

Vik updates without forUpdate. What happens?

Knowledge Check

conPeek(data, 0) on a container?


Next up: Object-Oriented X++ β€” classes, inheritance, QueryBuilder, and attributes.