Skip to main content

vs Data Events

Data events are a standard D365FO feature for subscribing to create, update, and delete operations behind a data entity. Activate an entity in the Data event catalogue, and D365FO prepares a message containing the entity data when a related record changes:

Data event catalogue - activating an entity

Measuring the performance impact of data events

Test scenario: activate the update event for the Released Products V2 entity, then use Trace Parser while updating 100 InventTableModule records in one transaction.

ttsbegin;
while select forupdate inventTableModule
order by inventTableModule.ItemId
{
inventTableModule.Price += 1;
inventTableModule.update();
}
ttscommit;

Inside each update

One row update in the call tree:

Trace Parser call tree of a single update with data events enabled

StepTime
The actual UPDATE statement~1 ms
BusinessEventsEntityChangeHandler::handleUpdating (pre-write)~6 ms
BusinessEventsEntityChangeHandler::handleUpdate (post-write)~35 ms
Complete InventTableModule.update() call~43 ms

A 1 ms database update becomes a ~43 ms application update — around 40 times slower before D365FO sends anything to the target endpoint.

Pre-commit processing extends the transaction

The per-record handlers are not the only cost. For the transaction containing 100 updates, ttsNotifyPreCommit takes approximately 19.6 seconds:

Trace Parser call tree of ttsNotifyPreCommit

BusinessEventsDataEventChangeProcessorBase::processTransactionPrecommit accounts for 17.5 seconds of that time.

This delay also extends lock duration. Data Events and dual-write pre-commit processing run after work that includes the InventSum update. Adding seconds at this stage keeps database locks active longer and can block other users or batch processes working with the same items.

Filtering is limited

Once a data event is active, its cost applies to every update of every table behind that entity. Company-specific events can be restricted by legal entity, but there is no source-side field filtering (track only selected fields) or record filtering (track only a subset of records). Filtering a message at the endpoint does not remove the work already performed in the D365FO transaction.

External Integration: an alternative way to publish D365FO changes

The Outbound Event-based message type covers the same "publish a change" requirement with the filtering and visibility that data events lack:

Typical uses include publishing changes to complex master data such as customers and vendors, or exporting documents after a business operation.

  • You write the condition. A DataEventHandler — or any other insert point — decides what qualifies. Comparing against orig() gives field-level filtering; any other check on the record gives record-level filtering:

    [DataEventHandler(tableStr(PurchTable), DataEventType::Updated)]
    public static void PurchTable_onUpdated(Common sender, DataEventArgs e)
    {
    PurchTable purchTable = sender as PurchTable;
    if (purchTable.DocumentState == VersioningDocumentState::Confirmed &&
    purchTable.orig().DocumentState != purchTable.DocumentState) // only this change, only these records
    {
    DEVIntegTutorialExportPurchOrder::construct().insertFromPurchTable(purchTable);
    }
    }
  • The transaction stays fast. All that happens inside it is one row inserted into the Export document log with status To send. Building the payload, calling the service, and processing the response all happen after the transaction — in the user session or in a batch job.

  • Every document has a single status. Each document has one log row, and another qualifying update reuses it and sets the status back to To send. The payload is built at export time from the current data. Users can see the send status, history, attempts, and message body when full logging is enabled. A data event provides no equivalent view.

  • Any target and any format. Azure Service Bus, a REST/SOAP service, or a file — with the payload you build, not the entity schema.

CapabilityData EventsExternal Integration
Impact on the source transactionEvent processing runs inside each update and again during pre-commit, extending the source transaction time✅ One insert into a log table; the payload is built and sent after the transaction
Field-level (horizontal) filteringNot available — any field change raises the event✅ Your X++ condition decides which field changes matter
Record-level (vertical) filteringNot available — every record of the table✅ Any condition on the record, including company and status
Per-record visibilityNo status per record✅ One Export document log row per document — status, history, retries
Delivery orderOne message per event; delivery order is not guaranteed✅ Repeated updates reuse one log row; the payload built at export time contains the current data
PayloadFixed entity schema✅ Any format you build in X++