# Table Methods in AX 2012: X++ Reference for Overrides, Kernel Helpers and Conventions

> Source: https://netray.co/tools/ax-2012-xpp-table-methods-reference
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: Free Tools

## Short answer

The xRecord methods you override or call on an AX 2012 table, when the kernel calls each one, and the find/exist conventions, with tips that keep set-based operations fast.

## Category

microsoft-dynamics

## Erp

Microsoft Dynamics AX 2012

## Audience

- X++ developer
- AX technical consultant
- ERP administrator

## Tool

**Kind:** worksheet

**Mode:** reference

**Columns:**

#### Method

**Key:** method

**Type:** text

#### Kind

**Key:** kind

**Type:** select

**Options:**

- Override
- Kernel helper
- Static convention

#### When it runs

**Key:** whenCalled

**Type:** text

#### Returns

**Key:** returns

**Type:** text

#### Tip

**Key:** tip

**Type:** text

**Rows:**

**Method:** initValue

**Kind:** Override

**When Called:** When a new record is created in a form, or when you call it in code after declaring a buffer

**Returns:** void

**Tip:** Put field defaults here, not in insert, so the user sees them before saving

**Method:** validateField

**Kind:** Override

**When Called:** When a field is changed in a form, before modifiedField

**Returns:** boolean

**Tip:** Use a switch on the field id; return checkFailed(...) to block the value

**Method:** modifiedField

**Kind:** Override

**When Called:** After a field value is accepted in a form

**Returns:** void

**Tip:** Cascade dependent fields here (for example fill a name from an account)

**Method:** validateFieldValue

**Kind:** Override

**When Called:** Field validation variant that receives field name and array index

**Returns:** boolean

**Tip:** Use for array fields; check your xRecord node to see if your kernel exposes it

**Method:** modifiedFieldValue

**Kind:** Override

**When Called:** Modified-field variant that receives field name and array index

**Returns:** void

**Tip:** Pair with validateFieldValue when overriding array field behaviour

**Method:** validateWrite

**Kind:** Override

**When Called:** On save from a form. Not called automatically by insert() or update() in code

**Returns:** boolean

**Tip:** Call it explicitly before insert or update in import code; never write to the database inside it

**Method:** validateDelete

**Kind:** Override

**When Called:** On delete from a form. Not called automatically by delete() in code

**Returns:** boolean

**Tip:** Block deletes of records still in use; call it yourself in batch code

**Method:** insert

**Kind:** Override

**When Called:** When code or a form inserts a record

**Returns:** void

**Tip:** super() performs the actual write; logic before super() can still change fields

**Method:** update

**Kind:** Override

**When Called:** When code or a form updates a selected-for-update record

**Returns:** void

**Tip:** Compare this.field with this.orig().field to react only to real changes

**Method:** delete

**Kind:** Override

**When Called:** When code or a form deletes a record

**Returns:** void

**Tip:** Delete actions run in super(); keep extra cleanup inside the same transaction

**Method:** doInsert

**Kind:** Kernel helper

**When Called:** Only when you call it

**Returns:** void

**Tip:** Writes the record without calling the overridden insert(); use for data fixes only

**Method:** doUpdate

**Kind:** Kernel helper

**When Called:** Only when you call it

**Returns:** void

**Tip:** Bypasses the overridden update(); business logic in update() is skipped

**Method:** doDelete

**Kind:** Kernel helper

**When Called:** Only when you call it

**Returns:** void

**Tip:** Bypasses the overridden delete(); business logic in delete() is skipped, so use for data fixes only

**Method:** aosValidateInsert

**Kind:** Override

**When Called:** On the AOS for every insert, from any client or code

**Returns:** boolean

**Tip:** Server-side guard that cannot be bypassed by client code

**Method:** aosValidateUpdate

**Kind:** Override

**When Called:** On the AOS for every update

**Returns:** boolean

**Tip:** Keep it light; it runs for every row

**Method:** aosValidateDelete

**Kind:** Override

**When Called:** On the AOS for every delete

**Returns:** boolean

**Tip:** Use for hard rules such as never delete posted records

**Method:** aosValidateRead

**Kind:** Override

**When Called:** On the AOS when a record is read

**Returns:** boolean

**Tip:** Very hot path; avoid queries here

**Method:** skipDataMethods

**Kind:** Kernel helper

**When Called:** Call before update_recordset, delete_from or insert_recordset

**Returns:** boolean

**Tip:** skipDataMethods(true) stops per-row calls to overridden update/delete/insert so set-based statements stay set-based

**Method:** skipDatabaseLog

**Kind:** Kernel helper

**When Called:** Call before a set-based operation

**Returns:** boolean

**Tip:** Database logging on a table forces row-by-row; skip only if audit rules allow

**Method:** skipEvents

**Kind:** Kernel helper

**When Called:** Call before a set-based operation

**Returns:** boolean

**Tip:** Alerts and events also force row-by-row processing

**Method:** skipDeleteActions

**Kind:** Kernel helper

**When Called:** Call before delete_from

**Returns:** boolean

**Tip:** Skips delete actions; you then own referential cleanup

**Method:** skipAosValidation

**Kind:** Kernel helper

**When Called:** Call before a write

**Returns:** boolean

**Tip:** Skips aosValidate methods; data migration only

**Method:** orig

**Kind:** Kernel helper

**When Called:** Any time after a record is selected

**Returns:** Common (the record as last read)

**Tip:** Use in update() to detect changed fields

**Method:** reread

**Kind:** Kernel helper

**When Called:** When you call it

**Returns:** void

**Tip:** Reloads the buffer from the database, for example after another process changed it

**Method:** data

**Kind:** Kernel helper

**When Called:** When you call it

**Returns:** Common

**Tip:** Copies field values from another buffer of the same table: target.data(source)

**Method:** clear

**Kind:** Kernel helper

**When Called:** When you call it

**Returns:** void

**Tip:** Empties the buffer before reuse in a loop

**Method:** isFormDataSource

**Kind:** Kernel helper

**When Called:** When you call it

**Returns:** boolean

**Tip:** Branch logic that should run only when the record is bound to a form

**Method:** caption

**Kind:** Override

**When Called:** When a form shows the record caption

**Returns:** str

**Tip:** Return the key and name so the form title is meaningful

**Method:** postLoad

**Kind:** Override

**When Called:** After a record is fetched from the database

**Returns:** void

**Tip:** Runs for every fetched row; avoid database calls here

**Method:** renamePrimaryKey

**Kind:** Kernel helper

**When Called:** From the Rename function or your code

**Returns:** void

**Tip:** Cascades a key change to related tables; slow on large data

**Method:** toolTipRecord

**Kind:** Override

**When Called:** When the client shows a tooltip for the record

**Returns:** str

**Tip:** Keep it to a short description

**Method:** find

**Kind:** Static convention

**When Called:** Called by your code: Table::find(key, forUpdate)

**Returns:** The table buffer

**Tip:** select firstonly with an optional forUpdate parameter; not a kernel method

**Method:** exist

**Kind:** Static convention

**When Called:** Called by your code: Table::exist(key)

**Returns:** boolean

**Tip:** select firstonly RecId only, for speed

**Method:** checkExist

**Kind:** Static convention

**When Called:** Called by your code before using a key

**Returns:** boolean

**Tip:** Wraps exist() and adds checkFailed with txtNotExist()

**Method:** txtNotExist

**Kind:** Static convention

**When Called:** Called by checkExist

**Returns:** str (label text)

**Tip:** Return a label such as "Record %1 does not exist"

**Notes:** Find table methods in the AOT under Data Dictionary > Tables > your table > Methods. Right-click Methods > Override method lists the xRecord methods the kernel lets you override. The write() method belongs to the form data source, not the table. find, exist, checkExist and txtNotExist are Microsoft coding conventions you add as static methods, not kernel methods.

## Content

**Intro:** This reference lists the table methods you override or call in Dynamics AX 2012 (RTM, R2, R3), split into kernel overrides, kernel helpers and the static find/exist conventions. Each row says when the kernel calls the method and the tip that avoids the most common bug or slowdown.

**Sections:**

#### How to use it

**Body:** Search for the method you are about to override or the behaviour you see. The Kind column tells you whether the kernel calls the method (Override), whether you call it yourself (Kernel helper), or whether it is a convention you write (Static convention). The When it runs column is the important one: validateWrite and validateDelete run on form saves, not on insert() or update() from code, which is why import jobs so often write invalid data. Export the table and add your own team rules as extra rows.

**Bullets:**

- Defaults: initValue
- Field rules: validateField then modifiedField
- Record rules: validateWrite, validateDelete
- Hard server rules: aosValidateInsert, aosValidateUpdate, aosValidateDelete

#### Where this lives in AX 2012

**Body:** Open the AOT (Ctrl+D), expand Data Dictionary > Tables, pick the table and expand Methods. Right-click Methods > Override method to add an xRecord override; right-click Methods > New method for static find and exist. The X++ editor opens the method; super() calls the kernel behaviour. In AX 2012 the code is compiled to p-code and, for server code, also to CIL, so run a full CIL compile after changes that batch or services use.

#### Worked example: find and exist on a custom table

**Body:** For a table XyzMachineTable with unique index on MachineId, write: static XyzMachineTable find(XyzMachineId _id, boolean _forUpdate = false) { XyzMachineTable rec; rec.selectForUpdate(_forUpdate); select firstonly rec where rec.MachineId == _id; return rec; } and static boolean exist(XyzMachineId _id) { return _id && (select firstonly RecId from XyzMachineTable where XyzMachineTable.MachineId == _id).RecId != 0; }. Add checkExist that calls exist and returns checkFailed(strFmt(XyzMachineTable::txtNotExist(), _id)) when missing.

#### Gotchas: set-based operations

**Body:** insert_recordset, update_recordset and delete_from fall back to row-by-row processing when the table overrides insert, update or delete, has database logging or alerts enabled, or has delete actions (for delete_from). Call skipDataMethods(true), skipDatabaseLog(true), skipEvents(true) or skipDeleteActions(true) on the buffer only when skipping that logic is correct. Remember that doInsert, doUpdate and doDelete bypass your overrides entirely, and that validateWrite is never called by the kernel during code inserts.

**Cta:** If you want developers and support staff to ask questions about your AX 2012 data and code base in plain English on your own servers, we can set up that private AI layer.

**Faq:**

#### What are the main table methods in AX 2012?

**Answer:** The common overrides are initValue, validateField, modifiedField, validateWrite, validateDelete, insert, update, delete and the aosValidate methods. Kernel helpers include doInsert, doUpdate, doDelete, orig, reread, data, clear and the skip methods. find and exist are static conventions you write yourself.

#### Is validateWrite called when I call insert() in X++?

**Answer:** No. validateWrite runs when a form saves a record. In code you must call it yourself: if (rec.validateWrite()) rec.insert(); The same applies to validateDelete before delete(). aosValidateInsert and the other aosValidate methods do run on the server for every write.

#### What is the difference between insert and doInsert in AX 2012?

**Answer:** insert() calls your overridden insert method, which normally calls super() to write the row. doInsert() writes the row directly without calling your override, so any logic in insert() is skipped. Use doInsert only for controlled data fixes where that logic must not run.

#### Why is my update_recordset slow?

**Answer:** It is probably running row by row. AX falls back when the table overrides update(), has database logging or alerts, or other per-row logic. Call skipDataMethods(true) and the other skip methods on the buffer before the statement if bypassing that logic is acceptable, then test the row count.

## Search terms

table methods in ax 2012, ax 2012 table methods, x++ validatewrite insert, ax 2012 doinsert vs insert, ax 2012 skipdatamethods, ax dynamics 2012 tips and tricks, dynamics ax 2012 architecture

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
