✏️ Explanatory Question

What is the Query framework in Dynamics 365 Finance & Operations? Explain the roles of Query, QueryBuildDataSource, QueryBuildRange and QueryRun, and when you would use a dynamic query instead of a plain while select loop.

👁 8 Views
📘 Detailed Answer
🟢 Easy
💡

Answer with Explanation

D365 F&O • X++ INTERVIEW

Query & QueryBuildDataSource in Dynamics 365 Finance & Operations

Question 12 — Building dynamic queries in X++ with ranges, joins and sorting.

Interview Question

What is the Query framework in Dynamics 365 Finance & Operations? Explain the roles of Query, QueryBuildDataSource, QueryBuildRange and QueryRun, and when you would use a dynamic query instead of a plain while select loop.

Model Answer (Short)

The Query framework lets you build and run queries programmatically instead of hard-coding a while select. The key classes are: Query (the container), QueryBuildDataSource (QBDS) (a table added to the query), QueryBuildRange (a filter/where condition on a field), QueryBuildFieldList (which fields to fetch), and QueryRun (executes the query and iterates results). You use dynamic queries when the filter criteria, data sources or sort order are not known at compile time — for example driven by user input, a dialog, or a SysOperation contract — and because they can be surfaced to users and reused across forms, reports and batch jobs.

Core Classes

Class Role
Query The top-level container that holds one or more data sources.
QueryBuildDataSource Represents a table/view added to the query; joins are added as child data sources.
QueryBuildRange A filter (where condition) applied to a field of a data source.
QueryBuildFieldList Controls which fields are selected (for performance / aggregation).
QueryRun Executes the query and iterates the result set with next().
WHEN TO USE DYNAMIC QUERIES
Runtime filtersuser-driven criteriareusable across UI/reports/batch

Prerequisites (Rule 5)

  • Visual Studio with the Dynamics 365 developer tools.
  • A custom model / package for your objects.
  • Knowledge of the target table's fields and indexes for efficient ranges.
  • Use fieldNum() / tableNum() intrinsics for compile-time safety.

Code Example — Build a query with a range

Query                query = new Query();
QueryBuildDataSource qbds;
QueryBuildRange      qbr;
QueryRun             queryRun;

// Add the table as a data source
qbds = query.addDataSource(tableNum(InventTable));

// Add a filter: ItemGroupId == 'RAW'
qbr = qbds.addRange(fieldNum(InventTable, ItemGroupId));
qbr.value('RAW');

// Execute and iterate
queryRun = new QueryRun(query);
while (queryRun.next())
{
    InventTable inventTable = queryRun.get(tableNum(InventTable));
    info(inventTable.ItemId);
}

Adding an inner join

Query                query = new Query();
QueryBuildDataSource qbdsSales, qbdsCust;

qbdsSales = query.addDataSource(tableNum(SalesTable));

// Child data source joined to the parent
qbdsCust = qbdsSales.addDataSource(tableNum(CustTable));
qbdsCust.joinMode(JoinMode::InnerJoin);
qbdsCust.relations(false);   // define relation manually below
qbdsCust.addLink(fieldNum(SalesTable, CustAccount),
                 fieldNum(CustTable,  AccountNum));

Sorting and a date range

QueryBuildDataSource qbds = query.addDataSource(tableNum(SalesTable));

// Sort by CreatedDateTime descending
qbds.addSortField(fieldNum(SalesTable, CreatedDateTime), SortOrder::Descending);

// Range using SysQuery range value helper for a date interval
QueryBuildRange qbr = qbds.addRange(fieldNum(SalesTable, ShippingDateConfirmed));
qbr.value(SysQuery::range(mkDate(1,1,2026), mkDate(31,12,2026)));

Range types & expressions

QueryBuildRange qbr = qbds.addRange(fieldNum(SalesTable, SalesStatus));

// A "greater than or equal" style expression
qbr.value(strFmt('((%1 >= %2))',
    fieldStr(SalesTable, SalesStatus),
    any2int(SalesStatus::Backorder)));

// Force the range to be hidden/locked from the user if needed
qbr.status(RangeStatus::Locked);

Dynamic Query vs. while select

Aspect Dynamic Query while select
Criteria known At runtime (flexible) At compile time (fixed)
User-editable filters Yes (can bind to a dialog / form) No
Reusability High — share across UI, reports, batch Low — embedded in code
Readability for simple cases More verbose Concise
Best for Configurable, data-driven selection Simple, fixed selections

Points the interviewer wants to hear

  • Query holds data sources; QueryBuildDataSource is a table in the query.
  • QueryBuildRange = a where filter; QueryRun executes and iterates.
  • Joins are added as child data sources with a JoinMode and addLink.
  • Use dynamic queries when criteria are runtime / user-driven or must be reusable.
  • Use fieldNum()/tableNum() intrinsics for compile-time safety.

Likely Follow-up Questions

  • What is the difference between JoinMode::InnerJoin and ExistsJoin?
  • How do you filter a query with an expression range (e.g. greater-than)?
  • How can you bind a dynamic query to a SysOperation dialog or a form data source?
  • Why prefer SysQuery::range() for building range values?

Key Takeaway

The Query framework — Query, QueryBuildDataSource, QueryBuildRange and QueryRun — lets you build flexible, runtime-driven queries that plug into forms, reports and batch jobs. Reach for dynamic queries whenever selection criteria aren't fixed at compile time.