Extension, non overlayering

Nelle finance and operations apps il codice standard non si tocca. L’overlayering è stato dismesso: qualunque personalizzazione in X++ vive in un modello separato che referenzia il modello standard e si aggancia dall’esterno. Il vantaggio non è stilistico ma operativo: gli aggiornamenti della piattaforma vengono applicati da Microsoft senza dover riconciliare a mano il tuo codice, e il deployable package resta compilabile.

Gli strumenti di estensione del comportamento sono due, e l’esame ti chiede continuamente di sceglierne uno: Chain of Command ed event handler. Accanto ci sono le estensioni degli elementi AOT (table extension, form extension, EDT ed enum estesi), ma quelle riguardano la struttura, non la logica. Lo sviluppo avviene in Visual Studio con gli strumenti per finance and operations; gli ambienti si provisionano oggi con la Unified Developer Experience (UDE) dal Power Platform admin center, non necessariamente dall’ambiente LCS classico.

Chain of Command: avvolgere il metodo

La CoC si scrive in una classe final decorata con [ExtensionOf(classStr(...))] (o tableStr, formStr, formDataSourceStr), dichiarando un metodo con la stessa identica signature di quello da avvolgere. Dentro esegui codice prima della chiamata a next, codice dopo, e puoi alterare i parametri in ingresso e il valore restituito da next prima di ritornarlo.

Il punto che l’esame verifica sempre: la chiamata a next è imposta dal compilatore su ogni percorso di esecuzione. Non è una convenzione né una buona pratica: se un ramo if o un return anticipato non passa da next, la compilazione fallisce. L’unica eccezione è un metodo che il codice base ha esplicitamente marcato come sostituibile con [Replaceable(true)]. La CoC può avvolgere metodi public e protected e accedere ai membri protetti della classe estesa — cosa che un event handler statico non può fare.

Event handler pre e post

L’event handler non avvolge nulla: è un metodo static che il runtime invoca prima o dopo il metodo originale, dichiarato con [PreHandlerFor(...)] o [PostHandlerFor(...)] e con un parametro XppPrePostArgs. Da lì leggi e riscrivi i parametri, e nel post-event handler leggi il valore di ritorno e lo sostituisci con uno tuo.

Differenze che contano nelle scelte: un pre-event handler non può impedire l’esecuzione del metodo originale; l’ordine di esecuzione fra più handler sottoscritti allo stesso evento non è garantito; il codice è disaccoppiato ma non vede lo stato interno dell’oggetto. Gli handler sono la strada naturale per gli eventi dichiarati (delegate, eventi di tabella come inserting/inserted/updating, eventi di form), mentre la CoC è la strada naturale quando devi condizionare o arricchire un metodo esistente lavorando sui suoi dati.

Batch framework e unit test

Una classe eseguibile con un main() gira quando qualcuno la lancia. Il batch framework è un’altra cosa: il lavoro viene accodato come batch job composto da task, assegnato a un batch group, eseguito da un’istanza AOS abilitata come batch server, con ricorrenza, dipendenze fra task, stato persistito, storico e riesecuzione dopo un errore. È la risposta corretta ogni volta che lo scenario dice “non presidiato”, “notturno”, “ricorrente”, “volumi elevati” o “deve riprendere dopo un fallimento”. Il framework moderno per costruirlo è SysOperation (data contract, controller, service), con RunBaseBatch come eredità storica. L’orchestrazione dall’esterno passa dal Batch OData API.

I unit test si scrivono in X++ derivando da SysTestCase, con i metodi marcati [SysTestMethod], setUp e tearDown per il contesto e i vari assert* per le verifiche. Vanno in un modello di test dedicato che referenzia il modello sotto test, si eseguono dal Test Explorer di Visual Studio e, soprattutto, si automatizzano nella pipeline di build. Un test dipendente da dati preesistenti dell’ambiente non è un test: i dati vanno creati e ripuliti dal test stesso.

Trappole tipiche d’esame

  • Devi impedire l’esecuzione del metodo standard in certi casi → verifica prima se è [Replaceable(true)]: con la CoC ordinaria il compilatore ti obbliga comunque a chiamare next, e un pre-event handler non può saltare il metodo. Se non è sostituibile, la soluzione è agire sui dati o intercettare il chiamante.
  • Devi modificare il valore restituito da un metodo standard → post-event handler (o CoC dopo next): il pre-event handler tocca solo i parametri; il valore di ritorno non esiste ancora quando viene invocato.
  • Il metodo è private o marcato [Hookable(false)] → nessuna delle due strade: cerca un punto di aggancio pubblico più in alto, un delegate, o un evento di tabella/form.
  • Più estensioni devono agire in sequenza garantita → CoC, non event handler: l’ordine di invocazione degli handler sottoscritti allo stesso evento non è deterministico.
  • Elaborazione lunga, notturna, ricorrente e riavviabile → batch framework, non una runnable class: una classe con main() copre l’esecuzione manuale, non stato, schedulazione e retry.
  • Sistema esterno che deve schedulare o monitorare un job → Batch OData API: non serve inventare un custom service, e non è compito di un data project del Data management framework.