bcquality/microsoft/knowledge/events/use-ishandled-to-make-base-behaviour-overridable.md
Jesper Schulz-Wedde 2b5550c346
Some checks failed
Validate knowledge index / validate-index (push) Has been cancelled
Validate AL review fixtures / validate-review-fixtures (push) Has been cancelled
Validate frontmatter and structure / validate (push) Has been cancelled
Improve partner onboarding and documentation navigation (#174)
Lead with a complete plugin quick start and add task-oriented usage, troubleshooting, customization, and contribution guides. Preserve the broader plugin framing, correct conflicting contract guidance, support Agents folder reviews, and align repository validation. Convert existing sample references to clickable links without changing knowledge rules.

Co-authored-by: Jesper Schulz-Wedde <jesper.schulzwedde@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-09-09 17:31:03 +02:00

2.5 KiB

bc-version domain keywords technologies countries application-area
all
events
ishandled
overridable
onbefore
integration-event
extensibility
event-override
subscriber-hook
al
w1
all

Use the IsHandled pattern to make base behaviour overridable

Description

AL has no method overriding, so a procedure that runs its body unconditionally cannot be replaced by an extension without editing base code. The established Business Central seam for substituting default behaviour is the IsHandled pattern: the routine raises an OnBefore… integration event carrying a var IsHandled: Boolean, then exits early when a subscriber has set it. This hands a partner a sanctioned hook to replace the logic instead of overwriting the routine. LLMs trained on languages with inheritance emit routines whose logic always runs and expose no OnBefore/IsHandled seam, so the behaviour silently cannot be overridden.

Best Practice

Raise OnBeforeX(…, IsHandled) as the first step of the routine and guard with if IsHandled then exit; before any default logic runs. Declare the publisher [IntegrationEvent(false, false)] local procedure OnBeforeX(…; var IsHandled: Boolean) with an empty body, and keep IsHandled a var parameter so a subscriber can write to it. A subscriber that replaces the behaviour does its work and sets IsHandled := true; one that only augments leaves it untouched and guards with if IsHandled then exit; itself. Reserve the override hook for cases where a partner genuinely needs to replace logic — when the goal is only to react, a positive OnAfter event is the better seam.

See sample: use-ishandled-to-make-base-behaviour-overridable.good.al.

Anti Pattern

Two shapes. First, a routine whose default logic always runs because there is no OnBefore…/IsHandled hook at all — extensions cannot change it without overwriting base code. Second, a routine that raises OnBeforeX(IsHandled) but omits the if IsHandled then exit; guard, so the default logic still executes after a subscriber set IsHandled := true, duplicating work and side effects. Detection: an OnBefore publisher with a var IsHandled: Boolean parameter whose caller never tests IsHandled, or a public routine doing non-trivial work with no overridable seam.

See sample: use-ishandled-to-make-base-behaviour-overridable.bad.al.