Domain 3 β€” Module 2 of 5 40%
7 of 28 overall
Domain 3: Design and Develop AOT Elements Free ⏱ ~14 min read

Views, Queries, and Maps

Build reusable data retrieval layers in D365 F&O β€” AOT views as virtual tables, AOT queries as reusable data sources, and maps as abstraction over similar tables. Master the data abstraction tools tested on MB-500.

Three data abstraction tools

D365 F&O gives you three AOT objects for working with data above the raw table level: views, queries, and maps. Each solves a different problem.

Simple explanation

Think of a library.

A table is a bookshelf β€” it holds the actual books. A view is a reading list that pulls books from multiple shelves and shows them in one list. The books never move β€” the list just points to them. You can read the list but can’t rearrange the actual shelves through it.

A query is a search template saved in the catalogue system β€” β€œfiction, published after 2020, English only.” Anyone can use this saved search instead of writing their own, and they can add extra filters on top.

A map is like a universal borrowing form that works for both books and DVDs. The form has a β€œTitle” field that maps to β€œBook Title” on the book system and β€œDVD Title” on the DVD system. Same form, different underlying sources.

AOT Views

A view is a read-only virtual table defined by a query. It exists in the AOT, compiles to a SQL Server view, and can be used anywhere you’d use a table for reading data.

When to use views

ScenarioWhy a View Helps
Joining multiple tables for a reportOne data source instead of complex joins in every report
Aggregating data (SUM, COUNT, AVG)Computed at database level β€” faster than X++ loops
Simplifying form data sourcesForm binds to one view instead of juggling multiple tables
Backing a data entityView serves as the query layer behind an entity

Creating a view in Visual Studio

  1. Right-click project β†’ Add New Item β†’ Data Dictionary β†’ View
  2. Set the view name (e.g., AxionInspectionSummaryView)
  3. Expand Data Sources β€” add the base table and joined tables
  4. Define Fields β€” select which columns to expose
  5. Add Computed Columns for calculations or CASE expressions
  6. Set Group By fields for aggregation

Computed columns

Computed columns let you add logic that runs as SQL:

// Computed column: StatusText on AxionInspectionSummaryView
// Returns a human-readable status based on the enum value
private static server str statusText()
{
    DictEnum dictEnum = new DictEnum(enumNum(AxionInspectionStatus));
    str viewName = tableStr(AxionInspectionSummaryView);

    // Use SysComputedColumn helper methods
    str statusField = SysComputedColumn::comparisonField(
        viewName,
        identifierStr(AxionInspectionTable),
        fieldStr(AxionInspectionTable, Status)
    );

    str result = SysComputedColumn::switch(
        SysComputedColumn::cases(
            SysComputedColumn::caseExpr(
                SysComputedColumn::equalExpression(statusField, SysComputedColumn::returnLiteral(0)),
                SysComputedColumn::returnLiteral("Open")
            ),
            SysComputedColumn::caseExpr(
                SysComputedColumn::equalExpression(statusField, SysComputedColumn::returnLiteral(1)),
                SysComputedColumn::returnLiteral("In Progress")
            ),
            SysComputedColumn::caseExpr(
                SysComputedColumn::equalExpression(statusField, SysComputedColumn::returnLiteral(2)),
                SysComputedColumn::returnLiteral("Completed")
            )
        ),
        SysComputedColumn::returnLiteral("Unknown")
    );

    return result;
}
Scenario: Vik builds an inspection dashboard view

PM Fatima needs a dashboard showing inspection counts by status and inspector. Instead of writing complex X++ aggregation code, Vik creates an AOT view:

  • Data source: AxionInspectionTable joined to HcmWorker
  • Fields: InspectorName (from HcmWorker), Status
  • Group By: InspectorName, Status
  • Computed column: InspectionCount using SysComputedColumn::count()

The view runs as a single SQL query β€” far faster than selecting all inspections and looping in X++. Fatima’s dashboard form just binds to this view as its data source.

Question

Can you update data through an AOT view?

Click or press Enter to reveal answer

Answer

No. AOT views in D365 F&O are read-only. They compile to SQL Server views, and the runtime does not support insert/update/delete through them. To modify data, you must use the underlying tables directly.

Click to flip back

Question

What is a computed column in an AOT view?

Click or press Enter to reveal answer

Answer

A computed column is a server-side method that returns a SQL expression (not X++ logic). It's compiled into the view's SQL definition. Common uses: CASE expressions for status text, calculated fields, string concatenation. Uses the SysComputedColumn helper class.

Click to flip back

AOT Queries

An AOT query is a reusable, metadata-defined data retrieval object. Think of it as a saved SELECT statement that anyone can reference, extend, and compose with.

Queries vs views

Views are fixed SQL objects; queries are flexible runtime objects
FeatureAOT ViewAOT Query
Compiled toSQL Server viewRuntime query object (translated to SQL at execution)
Read/WriteRead-onlyCan be used for both read and write operations
AggregationYes β€” GROUP BY, SUM, COUNT in SQLLimited β€” primarily for row retrieval
Used as form data sourceYes β€” like a tableYes β€” QueryBuildDataSource
Modifiable at runtimeNo β€” fixed at compile timeYes β€” add ranges, data sources, and sorting in code
ExtensibleVia view extensionVia query extension
PerformanceSQL-level optimizationQuery plan varies based on runtime modifications

Query structure

// AOT Query: AxionOpenInspections
// Data Sources:
//   AxionInspectionTable
//     Ranges:
//       Status == Open (AxionInspectionStatus::Open)
//     Sorting:
//       InspectionDate (Descending)
//     Joined Data Sources:
//       HcmWorker (join on InspectorWorker == RecId)
//         Fields: Name

Using an AOT query in X++

// Using the AOT query as a starting point, then adding a filter
Query query = new Query(queryStr(AxionOpenInspections));
QueryBuildDataSource qbds = query.dataSourceTable(tableNum(AxionInspectionTable));

// Add an extra range at runtime
QueryBuildRange range = qbds.addRange(fieldNum(AxionInspectionTable, InspectionDate));
range.value(queryValue(today()));

QueryRun queryRun = new QueryRun(query);

while (queryRun.next())
{
    AxionInspectionTable inspection = queryRun.get(tableNum(AxionInspectionTable));
    info(strFmt("Inspection %1 on %2", inspection.InspectionId, inspection.InspectionDate));
}
Exam tip: QueryBuildRange values

Range values in AOT queries support special syntax:

  • Exact value: "Open" or queryValue(dateValue)
  • Range: "1..100" β€” between 1 and 100
  • Not equal: "!Open" β€” everything except Open
  • Wildcard: "INS*" β€” starts with INS
  • Or: "Open,InProgress" β€” matches either value
  • Empty: "" β€” no filter (shows all)

The exam may ask which syntax returns the correct data set.

Question

What is the key advantage of an AOT query over writing ad-hoc X++ select statements?

Click or press Enter to reveal answer

Answer

Reusability and composability. An AOT query is defined once in metadata and can be used by multiple forms, reports, and code paths. It can also be extended via query extensions and modified at runtime (adding ranges, joins, sorts) without rewriting the base query.

Click to flip back

Maps

Maps are an abstraction layer that lets you write code that works against multiple tables with similar structures, without knowing at compile time which table you’re using.

Simple explanation

Think of a universal remote control.

Your living room has a Samsung TV, a Sony sound bar, and an LG Blu-ray player. A universal remote has one β€œVolume Up” button that works on all three devices β€” but behind the scenes, it sends different signals to each brand.

A map is like that universal remote. It has field names like β€œAccountNum” and β€œName” that map to real fields on different tables β€” SalesTable might have β€œCustAccount” while PurchTable has β€œVendAccount.” Your code talks to the map’s β€œAccountNum,” and the map translates to the right field on whichever table is active.

Map structure

// Map: AxionDocumentMap
// Fields:
//   DocumentId      β†’ maps to SalesTable.SalesId AND PurchTable.PurchId
//   AccountNum      β†’ maps to SalesTable.CustAccount AND PurchTable.OrderAccount
//   DocumentDate    β†’ maps to SalesTable.CreatedDateTime AND PurchTable.CreatedDateTime
//
// Mapped tables:
//   SalesTable
//   PurchTable

Using a map in X++

// A method that works on either sales or purchase documents
public void printDocumentInfo(AxionDocumentMap _documentMap)
{
    info(strFmt("Document: %1, Account: %2, Date: %3",
        _documentMap.DocumentId,
        _documentMap.AccountNum,
        _documentMap.DocumentDate));
}

// Calling with a SalesTable record β€” the map resolves automatically
SalesTable salesOrder;
select firstonly salesOrder;

AxionDocumentMap docMap;
docMap = salesOrder;   // implicit mapping
this.printDocumentInfo(docMap);
Scenario: Sophie encounters a legacy map

Sophie Chen at Ferris Industries is debugging a report that shows β€œDocument Number” for both sales and purchase orders. The code uses AddressMap to get a single postal address regardless of whether the source is a customer, vendor, or warehouse address table.

Mentor Carl explains: β€œMaps were the AX 2012 way to write polymorphic code over tables. Today we’d use an interface, but a lot of standard code still uses maps. Don’t create new maps β€” but you need to understand existing ones.”

Question

What is the modern alternative to maps in D365 F&O?

Click or press Enter to reveal answer

Answer

Interfaces and table inheritance. Interfaces let tables implement a common contract with methods, while table inheritance creates a base table with shared fields. Both are extensible via the extension model, unlike maps. New development should avoid maps.

Click to flip back

Choosing between views, queries, and maps

Match the data abstraction tool to the problem
NeedBest ChoiceWhy
Read-only aggregation (SUM, COUNT)ViewCompiled to SQL β€” runs at database level, fastest for aggregation
Reusable WHERE clause across formsQueryDefine ranges once, reuse everywhere, modify at runtime
Backing data source for a data entityViewEntities often need joined/flattened data β€” views provide this cleanly
Polymorphic code over similar tablesInterface (or map if legacy)Interfaces are extensible and modern; maps still work but can't be extended
Dynamic filtering on a list pageQueryUser filters modify the query at runtime β€” views can't accept runtime parameters
Flat join of 3+ tables for exportViewSingle virtual table simplifies the export mapping
Knowledge Check

Vik needs to create a dashboard that shows the total number of inspections per status per month. The data comes from AxionInspectionTable. Which AOT object should he use?

Knowledge Check

Sophie needs to build a list page that shows open purchase orders. Users should be able to add their own filters at runtime (by vendor, date range, etc.). The base filter is 'Status = Open'. Which approach is correct?

Knowledge Check

A developer creates a new map in D365 F&O to abstract over SalesTable and PurchTable for a reporting module. A colleague suggests using an interface instead. Which statement is correct?


Next up: Building Data Entities β€” create OData-enabled data entities for import, export, and integrations.