Utilizzo del framework SysExtension per scoprire quale sottoclasse istanziare in Dynamics AX 2012
Pubblicato: 16 febbraio 2025 alle ore 00:25:47 UTC
Ultimo aggiornamento: 12 gennaio 2026 alle ore 08:43:01 UTC
In questo articolo viene descritto come utilizzare il framework SysExtension, poco conosciuto, in Dynamics AX 2012 e Dynamics 365 for Operations per creare istanze di sottoclassi basate su decorazioni di attributi, consentendo una progettazione facilmente estensibile di una gerarchia di classi di elaborazione.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
Le informazioni contenute in questo articolo si basano su Dynamics AX 2012 R3. Potrebbero essere valide anche per altre versioni. (Aggiornamento: posso confermare che le informazioni contenute in questo articolo sono valide anche per Dynamics 365 for Operations)
Quando si implementano classi di elaborazione in Dynamics AX, spesso ci si trova a dover creare una gerarchia di classi in cui ogni sottoclasse corrisponde a un valore enum o presenta un altro accoppiamento di dati. Una soluzione classica prevede quindi l'inserimento di un metodo di costruzione nella superclasse, che dispone di uno switch che determina quale classe istanziare in base all'input.
In linea di principio funziona bene, ma se si hanno molti input diversi possibili (molti elementi in un enum o forse l'input è una combinazione di diversi valori), la manutenzione può diventare noiosa e soggetta a errori e il progetto presenta sempre lo svantaggio che sarà necessario modificare il metodo di costruzione se si aggiunge una nuova sottoclasse o si apportano modifiche alla sottoclasse da utilizzare in base all'input.
Fortunatamente, esiste un modo molto più elegante, ma purtroppo anche molto meno conosciuto, per farlo: utilizzare il framework SysExtension.
Questo framework sfrutta gli attributi che puoi usare per decorare le tue sottoclassi, in modo che il sistema sia in grado di capire quale sottoclasse usare per gestire cosa. Avrai comunque bisogno di un metodo di costruzione, ma se fatto correttamente, non dovrai mai modificarlo quando aggiungi nuove sottoclassi.
Consideriamo un esempio immaginario e immaginiamo di voler implementare una gerarchia che esegua un'elaborazione basata sulla tabella InventTrans. L'elaborazione da eseguire dipende da StatusReceipt e StatusIssue dei record, nonché dal fatto che i record siano correlati a SalesLine, PurchLine o a nessuno dei due. Stiamo già considerando molte combinazioni diverse.
Supponiamo che tu sappia che per ora devi gestire solo una manciata di combinazioni, ma che nel tempo ti verrà chiesto di saper gestire sempre più combinazioni.
Semplifichiamo la situazione e diciamo che per ora devi gestire solo i record correlati a SalesLine con StatusIssue di ReservPhysical o ReservOrdered; tutte le altre combinazioni possono essere ignorate per ora, ma poiché sai che dovrai gestirle in seguito, vorrai progettare il tuo codice in modo che sia facilmente estensibile.
Per ora la tua gerarchia potrebbe apparire più o meno così:
- MyProcessorMyProcessor_SalesMyProcessor_Sales_ReservOrderedMyProcessor_Sales_ReservPhysical
Ora, potresti facilmente implementare un metodo nella superclasse che istanzia una sottoclasse basata su un enum ModuleInventPurchSales e StatusIssue. Ma in tal caso dovrai modificare la superclasse ogni volta che aggiungi una sottoclasse, e questo non è esattamente il concetto di ereditarietà nella programmazione orientata agli oggetti. Dopotutto, non è necessario modificare RunBaseBatch o SysOperationServiceBase ogni volta che aggiungi un nuovo processo batch.
In alternativa, è possibile utilizzare il framework SysExtension. Ciò richiederà l'aggiunta di un'altra classe, che dovrà estendere SysAttribute. Questa classe verrà utilizzata come attributo con cui decorare le classi di elaborazione.
Questa classe è molto simile a come si creerebbe una classe di contratto dati per un'implementazione SysOperation, in quanto avrà alcuni membri dati e metodi parm per ottenere e impostare tali valori.
Nel nostro caso, la ClassDeclaration potrebbe avere un aspetto simile a questo:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Devi creare un metodo new() per istanziare tutti i membri dei dati. Se preferisci, puoi assegnare ad alcuni o a tutti i valori predefiniti, ma io non l'ho fatto.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Dovresti anche implementare un metodo parm per ogni membro dati, ma li ho omessi perché sono sicuro che sai come farlo, altrimenti consideriamolo un esercizio ;-)
Ora puoi usare la tua classe di attributi per decorare ciascuna delle tue classi di elaborazione. Ad esempio, le dichiarazioni di classe potrebbero apparire così:
StatusIssue::None,
StatusReceipt::None)]
class MyProcessor_Sales extends MyProcessor
{
}
[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
StatusIssue::ReservOrdered,
StatusReceipt::None)]
class MyProcessor_Sales_ReservOrdered extends MyProcessor_Sales
{
}
[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
StatusIssue::ReservPhysical,
StatusReceipt::None)]
class MyProcessor_Sales_ReservPhysical extends MyProcessor_Sales
{
}
Naturalmente puoi assegnare alle tue classi il nome che preferisci, l'importante è decorarle con attributi che corrispondano al tipo di elaborazione che svolgono. (Tieni presente che in Dynamics AX esistono delle convenzioni di denominazione per le gerarchie di classi ed è sempre una buona idea seguirle, se possibile).
Ora che hai decorato le tue classi per identificare il tipo di elaborazione eseguita da ciascuna di esse, puoi sfruttare il framework SysExtension per creare istanze di oggetti delle sottoclassi in base alle tue esigenze.
Nella tua superclasse (MyProcessor), potresti aggiungere un metodo di costruzione come questo:
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
MyProcessor ret;
MyProcessorSystemAttribute attribute;
;
attribute = new MyProcessorSystemAttribute( _module,
_statusIssue,
_statusReceipt);
ret = SysExtensionAppClassFactory::getClassFromSysAttribute(classStr(MyProcessor), attribute);
if (!ret)
{
// no class found
// here you could throw an error, instantiate a default
// processor instead, or just do nothing, up to you
}
return ret;
}
La parte davvero interessante, e in realtà l'oggetto (scusate il gioco di parole) di tutto questo post, è il metodo getClassFromSysAttribute() nella classe SysExtensionAppClassFactory. Questo metodo accetta il nome della superclasse di una gerarchia (e questa superclasse non deve necessariamente essere al vertice della gerarchia; significa semplicemente che saranno ammesse solo le classi che estendono questa classe) e un oggetto attributo.
Restituisce quindi un oggetto di una classe che estende la superclasse specificata ed è decorato con un attributo corrispondente.
Ovviamente, puoi aggiungere al metodo di costruzione tutta la validazione o la logica che desideri, ma la cosa importante è che, una volta implementato, non dovresti più modificarlo. Puoi aggiungere sottoclassi alla gerarchia e, a patto che tu ti assicuri di decorarle in modo appropriato, il metodo di costruzione le troverà anche se non esistevano al momento della sua scrittura.
Le prestazioni? Onestamente non ho provato a fare benchmark, ma il mio istinto mi dice che probabilmente offre prestazioni peggiori rispetto alla classica istruzione switch. Tuttavia, considerando che la maggior parte dei problemi di prestazioni in Dynamics AX sono causati dall'accesso al database, non mi preoccuperei troppo.
Naturalmente, se si sta implementando qualcosa che richiede la creazione rapida di migliaia di oggetti, potrebbe essere opportuno approfondire l'argomento, ma nei casi classici in cui si istanzia un singolo oggetto per eseguire un'elaborazione prolungata, dubito che abbia importanza. Inoltre, considerando il mio suggerimento per la risoluzione dei problemi (paragrafo successivo), sembra che il framework SysExtension si basi sulla memorizzazione nella cache, quindi in un sistema in esecuzione dubito che abbia un impatto significativo sulle prestazioni.
Risoluzione dei problemi: se il metodo di costruzione non trova le sottoclassi, anche se sei certo che siano decorate correttamente, potrebbe trattarsi di un problema di cache. Prova a cancellare le cache sia sul client che sul server. Non dovrebbe essere necessario riavviare l'AOS, ma potrebbe essere l'ultima risorsa.
Ulteriori letture
Se ti è piaciuto questo post, potrebbero piacerti anche questi suggerimenti:
- Panoramica rapida di Dynamics AX 2012 SysOperation Framework
- Come scorrere gli elementi di un enum dal codice X++ in Dynamics AX 2012
- Formattazione stringa con macro e strFmt in Dynamics AX 2012
