4 min readRishi

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 class and 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 valueChain of Command
Modify input parameters before standard logicChain of Command
Touch protected members of the classChain of Command
React to a row insert/update/deleteData event handler
Hook a method that exposes pre/post delegatesEvent handler
Add behaviour with zero coupling to other featuresEvent handler
Extend a method that is sealed to CoCEvent 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

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.