Domain 3 β€” Module 3 of 5 60%
8 of 28 overall
Domain 3: Design and Develop AOT Elements Free ⏱ ~15 min read

Building Data Entities

Create data entities in D365 F&O for OData integration and data import/export β€” backing tables, staging tables, field mapping, composite entities, and aggregate entities. This module covers building the entity; Domain 6 covers using it for data movement.

What is a data entity?

Simple explanation

Think of a front desk at a hotel.

Behind the scenes, the hotel has separate systems for rooms, billing, housekeeping, and guest preferences. But when you check in, you deal with one person at the front desk who handles everything β€” they translate your simple request (β€œI need a room”) into actions across multiple backend systems.

A data entity is that front desk. External systems (and import/export tools) talk to one entity β€” β€œCustomer” or β€œSales Order” β€” and the entity translates that into reads and writes across multiple underlying tables. The external system never needs to know about CustTable, DirPartyTable, LogisticsPostalAddress, or any of the 15+ tables that make up a β€œcustomer.”

Entities vs views vs tables

Data entities are the integration layer; views and tables serve different purposes
FeatureTableViewData Entity
Read/WriteFull CRUDRead-onlyFull CRUD (with validation logic)
OData endpointNoNoYes β€” automatic REST API
Staging tableNoNoYes β€” generated for import
Business logicTable methodsComputed columns onlyEntity methods (validate, default, transform)
Multiple backing tablesNo β€” single tableYes β€” via joinsYes β€” with field mapping to each table
Used in Data ManagementNoNoYes β€” primary tool for import/export
DenormalisedNormalised by designCan flatten joinsDesigned to be flat β€” one row = one business object

Creating a data entity in Visual Studio

Step-by-step: the entity wizard

  1. Right-click project β†’ Add New Item β†’ Data Model β†’ Data Entity
  2. Primary data source: Select the root table (e.g., AxionInspectionTable)
  3. Public entity name: The OData endpoint name (e.g., AxionInspections)
  4. Public collection name: Plural form for OData (e.g., AxionInspections)
  5. Enable public API: Yes for OData access, No for Data Management only
  6. Staging table: Auto-generated (e.g., AxionInspectionStaging)

Entity properties

PropertyPurposeTypical Value
IsPublicExposes entity as OData endpointYes for integrations, No for internal DMF only
PublicEntityNameOData entity name in the URLAxionInspections β†’ /data/AxionInspections
PublicCollectionNameOData collection nameSame as PublicEntityName (plural)
DataManagementEnabledAppears in Data Management workspaceYes
DataManagementStagingTableStaging table for importsAuto-generated
PrimaryKeyNatural key fields for the entityMaps to the table’s unique key
AutoPopulateFieldsWhether to auto-add all fields from data sourcesYes (then remove what you don’t need)
Scenario: Vik builds the Inspections entity

Axion Dynamics needs to import inspection records from their legacy quality system. Vik creates a AxionInspectionEntity with:

  • Primary data source: AxionInspectionTable
  • Joined data source: HcmWorker (to resolve inspector name to worker RecId)
  • Natural key: InspectionId
  • IsPublic: Yes (they also need OData for a Power App)

The entity flattens β€œInspectionId, Description, InspectorName, InspectionDate, Status” into one row β€” the external system doesn’t need to know that the inspector name comes from a different table.

Backing tables and data sources

An entity can have multiple data sources (backing tables) joined together:

Data source properties

PropertyPurpose
Is Read OnlyWhether this data source supports writes (No = writable)
Join typeInnerJoin, OuterJoin β€” how this table joins to its parent
Auto queryRelation-based auto-join or manual field mapping

Field mapping

Each entity field maps to a specific field on a specific data source:

// Entity: AxionInspectionEntity
// Field mappings:
//   InspectionId    β†’ AxionInspectionTable.InspectionId
//   Description     β†’ AxionInspectionTable.Description
//   InspectorName   β†’ HcmWorker.Name (read-only β€” resolved from worker table)
//   InspectionDate  β†’ AxionInspectionTable.InspectionDate
//   Status          β†’ AxionInspectionTable.Status
//
// InspectorName is mapped to HcmWorker (read-only data source)
// On import, the entity resolves "John Smith" β†’ HcmWorker.RecId β†’ stores RecId
Exam tip: natural key vs surrogate key

Data entities expose natural keys (human-readable values like InspectionId, CustAccount) β€” not surrogate keys (RecId). External systems don’t know RecId values.

During import, the entity must resolve natural keys to RecIds. For example: the import file contains β€œJohn Smith” as the inspector. The entity looks up HcmWorker to find the RecId for John Smith and stores the RecId in the inspection table.

If the lookup fails (name not found), the staging record gets an error status. The exam tests this resolution concept.

Staging tables

Every data entity with DataManagementEnabled = Yes gets a staging table β€” an intermediary where import data lands before being pushed to the real tables.

Import flow

External File β†’ Parse β†’ Staging Table β†’ Validate β†’ Target Tables
                         ↓ (errors stay here)
                      Error log with row-level details

Why staging tables matter

BenefitHow It Helps
ValidationData is validated before touching production tables
Error handlingFailed rows stay in staging with error details β€” fix and re-import
TransformationEntity methods can transform data between staging and target
PerformanceBulk insert into staging, then process in batches
RollbackIf import fails midway, production tables are untouched

Staging table structure

The staging table is auto-generated and mirrors the entity’s fields, plus:

  • DefinitionGroup β€” identifies which import job this row belongs to
  • ExecutionId β€” unique ID for this import run
  • IsSelected β€” whether this row should be processed
  • TransferStatus β€” Completed, Error, NotStarted
Question

What is the purpose of a staging table in the Data Management Framework?

Click or press Enter to reveal answer

Answer

A staging table is an intermediary where import data lands before being pushed to production tables. It enables validation (catch errors before they reach real data), error handling (failed rows stay in staging with details), and transformation (entity methods can modify data between staging and target).

Click to flip back

Entity methods (validation and defaulting)

Data entities support X++ methods that run during import/export:

// On AxionInspectionEntity

// Runs before each row is inserted into target tables
public boolean validateWrite()
{
    boolean ret = super();

    if (!this.InspectionId)
    {
        ret = checkFailed("Inspection ID is required");
    }

    if (this.InspectionDate > today())
    {
        ret = checkFailed("Inspection date cannot be in the future");
    }

    return ret;
}

// Maps staging fields to target table fields during import
public void mapEntityToDataSource(
    DataEntityRuntimeContext _entityCtx,
    DataEntityDataSourceRuntimeContext _dataSourceCtx)
{
    super(_entityCtx, _dataSourceCtx);

    // Custom mapping: resolve inspector name to RecId
    if (_dataSourceCtx.name() == dataEntityDataSourceStr(
        AxionInspectionEntity, AxionInspectionTable))
    {
        // Lookup logic handled by entity framework
        // when field has proper EDT and relation defined
    }
}

// Runs after insert to perform additional business logic
public void postLoad()
{
    super();
    // Post-processing after data loads from DB (for export scenarios)
}
Question

What X++ method on a data entity runs validation before each row is written to the target table?

Click or press Enter to reveal answer

Answer

validateWrite(). It returns a boolean β€” if false, the row fails and stays in the staging table with an error status. Use checkFailed() to add specific error messages. This is the primary place to enforce business rules during import.

Click to flip back

Composite entities

A composite entity groups multiple related entities into a single unit for import/export. Think: importing a sales order header AND its lines in one file.

When to use composite entities

ScenarioWhy Composite
Header + LinesSales order header and lines in one import file
Master + DetailsCustomer record + addresses + contacts in one package
Parent + ChildrenProduct master + variants + dimensions together

Structure

// Composite Entity: AxionInspectionCompositeEntity
//   Root Entity: AxionInspectionEntity (header)
//     Child Entity: AxionInspectionLineEntity (lines)
//       Link: InspectionId == InspectionId
Exam tip: composite entity import format

Composite entities require XML format for import β€” they don’t support CSV because CSV is flat and can’t represent parent-child hierarchy.

The XML structure nests child records inside parent records:

<AxionInspection>
  <InspectionId>INS-001</InspectionId>
  <Lines>
    <Line><LineNum>1</LineNum><Measurement>4.5</Measurement></Line>
    <Line><LineNum>2</LineNum><Measurement>3.8</Measurement></Line>
  </Lines>
</AxionInspection>

If the exam asks about importing header-line data from CSV, you’d need separate entity imports β€” one for headers, one for lines β€” not a composite entity.

Aggregate entities

Aggregate entities are read-only entities backed by aggregate measurements (pre-computed OLAP-style data from the Entity Store). They’re used for analytical scenarios, not transactional.

FeatureRegular EntityAggregate Entity
Data sourceTables in AXDBAggregate measurements (Entity Store)
Read/WriteRead and writeRead-only
Use caseTransactional operations, import/exportAnalytical dashboards, Power BI
PerformanceReal-time against OLAP or OLTPPre-computed, very fast for aggregation
Question

What is the key difference between a composite entity and an aggregate entity?

Click or press Enter to reveal answer

Answer

A composite entity groups multiple transactional entities (header + lines) for import/export β€” it's read/write. An aggregate entity is read-only and backed by pre-computed analytical data from the Entity Store β€” it's for reporting, not data movement.

Click to flip back

Entity development best practices

Vik’s checklist when building entities at Axion:

  1. Natural keys, not RecIds β€” external systems use human-readable identifiers
  2. Mark read-only data sources β€” joined reference tables should be read-only
  3. Validate in validateWrite() β€” catch bad data before it reaches production
  4. Test staging import β€” always test with the Data Management workspace, not just OData
  5. Prefix custom entities β€” AxionInspectionEntity, not just InspectionEntity
  6. Set IsPublic intentionally β€” only expose to OData if external systems need it
  7. Handle missing lookups β€” if a natural key can’t be resolved, the row should fail gracefully with a clear error
Scenario: Nikhil's first entity mistake

Junior Nikhil creates an entity that exposes RecId as the primary key. The integration team sends import files with RecId values from their test environment. In production, RecIds are different β€” every import fails.

Vik explains: β€œEntities expose natural keys like InspectionId, not RecIds. RecIds are database-specific surrogate keys β€” they’re different in every environment. The entity framework resolves natural keys to RecIds automatically.”

Knowledge Check

Vik creates AxionInspectionEntity with IsPublic = Yes and DataManagementEnabled = Yes. An external Power App needs to read inspection data via REST. Which URL pattern would the Power App use?

Knowledge Check

During a data import, the staging table shows 200 records with TransferStatus = 'Error'. The error message says 'Inspector not found' for each row. What is the most likely cause?

Knowledge Check

Axion needs to import inspection headers and their detail lines from a single file. The file is in CSV format. What should Vik do?


Next up: Forms, Menus, and UI Patterns β€” build user interfaces with form patterns, menu items, and the label system.