Chain of Command vs Event Handlers: Extending D365 F&O the Right Way
Since overlayering was retired, every customization in Dynamics 365 Finance & Operations is an extension — and you have two main tools: Chain of Command (CoC) and event handlers. They look interchangeable in a demo. They are not. Choosing the wrong one leads to logic that silently never runs, or worse, code that compiles but cannot touch the data it needs. Here is how to pick correctly.
The two tools in one minute
Chain of Command wraps a method. Your extension method has the same signature as the original, and you call next to invoke the standard logic. You run code before and after next, and you can see the return value.
[ExtensionOf(classStr(SalesLineType))]
final class SalesLineType_Extension
{
public boolean validateWrite()
{
boolean ret = next validateWrite(); // standard logic runs here
if (ret && this.salesLine.CustomMargin < 0)
{
ret = checkFailed("Custom margin cannot be negative.");
}
return ret;
}
}
Event handlers subscribe to a delegate or to a system-raised event (onValidatedWrite, onInserting, a custom delegate). They run at a fixed point and receive an arguments object — never the original method's local variables.
class SalesTable_EventHandlers
{
[DataEventHandler(tableStr(SalesTable), DataEventType::Inserting)]
public static void SalesTable_onInserting(Common sender, DataEventArgs e)
{
SalesTable salesTable = sender as SalesTable;
salesTable.CustomReference = SalesTable_EventHandlers::buildReference(salesTable);
}
}
When Chain of Command wins
Use CoC when you need any of the following:
- The return value, to inspect or override it.
- To alter parameters before the standard logic sees them.
- Access to the instance state — protected fields and methods of the class you extend are reachable from CoC (with
final classand matching visibility). - Guaranteed execution around the original, in a predictable order relative to
next.
CoC is the right default for extending class and form methods where you care about behaviour and results.
When event handlers win
Use event handlers when:
- The method is not extensible with CoC (for example, it is not marked
Hookable, or it is a property accessor). Many standard methods expose pre/post delegates precisely so you can hook them. - You only need to react, not to change the outcome — stamping a field on insert, kicking off a downstream process.
- You want loose coupling, so multiple independent features can subscribe without knowing about each other.
- Table CRUD events (
Inserting,Updated,Deleting) are exactly what you need.
The decision table
| You need to… | Use |
|---|---|
| Read or change a return value | Chain of Command |
| Modify input parameters before standard logic | Chain of Command |
| Touch protected members of the class | Chain of Command |
| React to a row insert/update/delete | Data event handler |
| Hook a method that exposes pre/post delegates | Event handler |
| Add behaviour with zero coupling to other features | Event handler |
| Extend a method that is sealed to CoC | Event handler (if a delegate exists) |
Gotchas that cost hours
You must call next in CoC — exactly once. Forget it and the standard logic never runs; call it twice and you double everything. There is no supported way to skip next to suppress base behaviour. If you need to prevent something, the design intent is usually a pre-event handler that validates and throws, not a CoC that swallows next.
Event handler order is not guaranteed. If two handlers subscribe to the same event, do not assume which runs first. Never build a feature that depends on another handler having already run.
Pre-event handlers cannot change what the method does unless the method reads its parameters back from the XppPrePostArgs object. For value types you can replace a parameter with args.setArg(); for the method's internal decisions you often cannot reach far enough — which is the moment to switch to CoC.
CoC needs matching signatures. If Microsoft changes a method signature in an update, your CoC breaks at compile time. That is a feature: you find out at build, not in production. Event handlers on a removed delegate fail more quietly, so review them after major updates.
A practical rule
Default to Chain of Command when you are shaping behaviour and results inside a class or form. Reach for event handlers when you are reacting to data changes, hooking a method that publishes delegates, or deliberately decoupling features. When both work, CoC usually gives you clearer control flow and compile-time safety — and that is worth more than the slight extra coupling.
Keep reading
Extending Data Entities in D365 Finance & Operations Without Breaking Upgrades
Add fields, computed columns, and validation to standard D365 Finance & Operations data entities the upgrade-safe way — with X++ examples and the staging-table traps to avoid.
Electronic Reporting in D365 Finance: Building Custom Formats Without Code
A practical guide to the Electronic Reporting (ER) framework in D365 Finance — data models, model mappings, and format configurations to produce custom files without X++.
Financial Dimensions in D365 Finance: How They Really Work
Default dimensions, ledger dimensions, account structures, and the DimensionAttributeValueCombination table explained — with X++ patterns for reading and building dimensions in D365 Finance.
Newsletter
New posts, straight to your inbox
One email per post. No spam, no tracking pixels, unsubscribe anytime.
Comments
No comments yet. Be the first.