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.
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
| Type | Description | Example |
|---|---|---|
| int / int64 | Integer | int count = 10; |
| real | Decimal | real price = 29.99; |
| str | String | str name = "Contoso"; |
| boolean | True/false | boolean active = true; |
| date / utcdatetime | Date values | date today = systemDateGet(); |
| container | Mixed-type collection | container 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;
| Keyword | Purpose |
|---|---|
| firstOnly | Return one record |
| order by | Sort results |
| group by / count / sum | Aggregates |
| join / outer join | Table joins |
| exists join / notexists join | Existence filter |
| forUpdate | Lock 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
| Function | Purpose |
|---|---|
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++.
| Rule | Catches | Fix |
|---|---|---|
| Missing label | Hardcoded UI strings | Use @SYS labels |
| Select without where | Unbounded queries | Add where clause |
| Unused variable | Declared not used | Remove it |
π¨βπ» Elena: βUse strFmt for all formatting, not concatenation. Cleaner, faster, translatable.β
Correct X++ to fetch one customer?
Vik updates without forUpdate. What happens?
conPeek(data, 0) on a container?
Next up: Object-Oriented X++ β classes, inheritance, QueryBuilder, and attributes.