SapotaCorp

Chain of Command in D365 F&O: Extend, Do Not Overlayer

Overlayering is gone in Dynamics 365 Finance and Operations, so standard behavior is now extended with Chain of Command and event handlers. Here is how a working developer decides between the two and avoids the gotchas that silently break base logic.

Chain of Command in D365 F&O: Extend, Do Not Overlayer

Key takeaways

  • Chain of Command lets you wrap a standard method and run code before or after the base logic while still seeing and changing the parameters and return value.
  • A CoC method that forgets to call next() silently drops the entire base implementation, which is the single most common way teams break standard behavior.
  • Event handlers (pre, post, and data events like OnInserting or OnValidatedWrite) hook into a method without wrapping it, but they cannot rewrite parameters the way CoC can.
  • CoC only works when the target method is extensible, so final, private, and hookable rules will block you before your code ever compiles.
  • Because every customization lives in its own model on top of sealed Microsoft code, platform updates apply cleanly instead of forcing a re-merge of overlayered objects.

If you came up through AX 2012, your instinct for changing standard behavior is to open the object, drop into the method, and edit the code in place. That instinct will get you nowhere in Dynamics 365 Finance and Operations, because overlayering is gone. The standard application is sealed, and you extend it from the outside. The two tools that matter are Chain of Command (CoC) for wrapping methods and event handlers for hooking into them, and most of the d365 chain of command pain people hit comes from not knowing which one to reach for and what each one quietly refuses to do.

What overlayering took away, and why that is good

In the old model you edited Microsoft's code directly. It felt powerful right up until the next cumulative update landed and you had to merge your changes against a new version of every object you had touched. Big AX builds spent real budget every upgrade just reconciling conflicts, and a missed merge meant a regression nobody noticed until month end.

The F&O model flips that. Your code lives in a separate model that sits on top of the standard packages, which stay untouched. Microsoft can ship a platform update and your extensions ride along, because nothing you wrote is tangled into their source. That is the whole point of the rewrite, and it is why an AX move is not a port but a redesign, something we covered in why an AX to F&O upgrade means redesign, not re-overlay. The tradeoff is that you no longer get to touch everything. You only get to extend what Microsoft marked as extensible, and that constraint shapes every decision below.

Chain of Command: wrap and next

Chain of Command lets you write a method with the exact same signature as a standard method, in an extension class, and your version wraps the original. Inside your method you call next yourMethod(args) to invoke the base implementation. Code before next runs first, code after next runs once the base logic has finished, and you can read or modify both the incoming parameters and the return value.

[ExtensionOf(classStr(SalesLineType))]
final class SalesLineType_MyExt_Extension
{
    public boolean checkUpdate()
    {
        // run before the standard validation
        boolean ret = next checkUpdate();

        if (ret && this.salesLine().MyCustomField == '')
        {
            ret = checkFailed("Custom field is required before update.");
        }

        return ret;
    }
}

The reason to use CoC is that you need to influence the flow, not just observe it. You can short circuit before next, you can adjust an argument before the base method sees it, and you can override the result on the way out. That control is the thing event handlers cannot give you.

Event handlers: hook without wrapping

An event handler subscribes to an event that fires around a method or a data operation. You get pre-events that fire before the method body, post-events that fire after it, and data events on tables such as OnInserting, OnInserted, OnUpdating, and OnValidatedWrite. Your handler is a static method that the kernel calls, and it receives an XppPrePostArgs object (for method events) or the table buffer (for data events).

class MyTableEventHandler
{
    [DataEventHandler(tableStr(SalesTable), DataEventType::ValidatedWrite)]
    public static void SalesTable_onValidatedWrite(Common sender, DataEventArgs e)
    {
        SalesTable salesTable = sender as SalesTable;
        ValidateEventArgs args = e as ValidateEventArgs;

        if (salesTable.MyCustomField == '')
        {
            args.parmValidateResult(false);
        }
    }
}

Handlers shine for reacting to something cleanly. A post-event on a method, or OnInserting on a table, is the right call when you want to fire side effects, stamp a field, or run extra validation without taking ownership of the method's flow. They also keep your code loosely attached, which is easy to reason about when several teams extend the same object.

Choosing between them

Reach for CoC when you need to change behavior: alter parameters before the standard logic runs, skip the base call under a condition, or rewrite the return value. Reach for an event handler when you only need to observe and react: log, validate, copy a value, or trigger a downstream process. If all you want is a post-method side effect, a handler is lighter and harder to get wrong. If you need to sit inside the call and steer it, CoC is the only option that works.

There is one hard limitation to keep in mind. An event handler cannot change the parameters the wrapped method actually uses. A pre-event handler can read the args and even flip a flag through XppPrePostArgs, but it does not control the value the base method runs with the way a CoC method does by mutating an argument before next. When the requirement is genuinely "the standard method must run with a different input," that is a CoC job, full stop.

The gotchas that actually bite

You must call next, or you silently delete the base logic. This is the number one CoC mistake. A CoC method replaces the standard method, and next is what re-invokes the original. Forget it and the base implementation never runs, with no error and no warning. The code compiles, the form opens, and three weeks later someone notices that a posting routine quietly stopped doing half its work. The exception to the rule is deliberate: you may legitimately skip next to suppress the base behavior, but that must be a conscious decision you can defend, not an oversight.

The method has to be extensible, and the rules are fussy. CoC needs the target method to be hookable. Methods marked final cannot be wrapped, private methods are off limits, and some methods are excluded from extensibility by attributes on the class. You will discover this at compile time when the extension simply will not build, and the fix is usually to find a different, extensible seam rather than to fight the one you wanted. Static methods follow their own rules too. Plan your extension point before you write the body, because the cleanest looking method is often the one Microsoft sealed.

Multiple CoC extensions chain in an order you do not fully control. When two models both wrap the same method, they form a chain, and each one's next calls into the following extension. That is by design, but it means your "after next" code may run before or after another team's depending on model load order. Keep your wrapped logic self contained and avoid assumptions about what else is in the chain.

Data events are not a transaction boundary. OnInserting and OnUpdating fire inside the write, so throwing from them rolls back the operation, which is often what you want for validation. But heavy work in a data event runs on every row and inside the transaction, so it is the wrong place for slow calls or external integration. Stamp the buffer there and push the expensive work elsewhere.

Takeaway

The F&O extension model trades the freedom to edit anything for the certainty that updates will not break your work. Use Chain of Command when you need to wrap a method and steer its flow, always call next unless you have a clear reason not to, and confirm the method is extensible before you commit to it. Use event handlers when you only need to react around a method or a data write, and accept that they cannot rewrite parameters the way CoC can. Get the choice right and your customizations survive every platform update with no merge and no drama.

If you are planning an F&O build or untangling extensions that fight the standard application, we do this every day. See how we work on /service, or tell us about your environment on /contact and we will help you pick the right seam.

Contact Us Now

Share Your Story

We build trust by delivering what we promise – the first time and every time!

We'd love to hear your vision. Our IT experts will reach out to you during business hours to discuss making it happen.

WHY CHOOSE US

"Collaborate, Elevate, Celebrate where Associates - Create Project Excellence"

SapotaCorp beyond the IT industry standard, we are

  • Certificated
  • Assured quality
  • Extra maintenance

Tell us about your project