Miklix

Utilizzo del framework SysExtension per scoprire quale sottoclasse istanziare in Dynamics AX 2012

Pubblicato: 16 febbraio 2025 alle ore 00:25:47 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 in base alle decorazioni degli attributi, consentendo una progettazione facilmente estensibile di una gerarchia di classi di elaborazione.


Questa pagina è stata tradotta automaticamente dall'inglese per renderla accessibile al maggior numero di persone possibile. Purtroppo, la traduzione automatica non è ancora una tecnologia perfezionata, quindi possono verificarsi degli errori. Se preferite, potete consultare la versione originale in inglese qui:

Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012

Le informazioni contenute in questo post si basano su Dynamics AX 2012 R3. Potrebbero essere valide o meno 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 di fronte alla creazione di una gerarchia di classi in cui ogni sottoclasse corrisponde a un valore enum o ha un altro accoppiamento di dati. Un design classico consiste quindi nell'avere un metodo di costruzione nella superclasse, che ha 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 sua manutenzione può diventare noiosa e soggetta a errori e la progettazione 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 a quale input.

Fortunatamente, esiste un modo molto più elegante, ma purtroppo anche molto meno conosciuto, per farlo: si ricorre al framework SysExtension.

Questo framework sfrutta gli attributi che puoi usare per decorare le tue sottoclassi per far sì che il sistema sia in grado di capire quale sottoclasse deve essere usata per gestire cosa. Avrai ancora bisogno di un metodo di costruzione, ma se fatto correttamente, non dovrai mai modificarlo quando aggiungi nuove sottoclassi.

Diamo un'occhiata a un esempio immaginario e diciamo che stai per implementare una gerarchia che esegue una sorta di elaborazione basata sulla tabella InventTrans. Quale elaborazione eseguire dipende da StatusReceipt e StatusIssue dei record, nonché dal fatto che i record siano correlati a SalesLine, PurchLine o a nessuno dei due. Già ora, stai esaminando molte combinazioni diverse.

Supponiamo che tu sappia che per ora devi gestire solo una manciata di combinazioni, ma che sai anche che col tempo ti verrà chiesto di saper gestire sempre più combinazioni.

Semplifichiamo la situazione e diciamo che per ora devi gestire solo i record relativi a SalesLine con StatusIssue di ReservPhysical o ReservOrdered; per ora puoi ignorare tutte le altre combinazioni, 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 assomigliare a questa:

  • Il mio processore
    • Il mio processore_Vendite
      • MyProcessor_Sales_ReservOrdered
      • MyProcessor_Sales_ReservPhysical

Ora, potresti facilmente implementare un metodo nella superclasse che istanzia una sottoclasse basata su un ModuleInventPurchSales e un enum StatusIssue. Ma poi dovrai modificare la superclasse ogni volta che aggiungi una sottoclasse, e questa non è proprio l'idea di ereditarietà nella programmazione orientata agli oggetti. Dopotutto, non hai bisogno di modificare RunBaseBatch o SysOperationServiceBase ogni volta che aggiungi un nuovo batch job.

Invece, puoi usare il framework SysExtension. Ciò richiederà di aggiungere un'altra classe, che deve estendere SysAttribute. Questa classe verrà usata come attributo con cui puoi decorare le tue classi di elaborazione.

Questa classe è molto simile a quella che si creerebbe per 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:

class MyProcessorSystemAttribute extends SysAttribute
{
    ModuleInventPurchSales  module;
    StatusIssue             statusIssue;
    StatusReceipt           statusReceipt
}

Devi creare un metodo new() per istanziare tutti i membri dei dati. Se vuoi, puoi dare ad alcuni o a tutti i valori predefiniti, ma io non l'ho fatto.

public void new(ModuleInventPurchSales  _module,
                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 qui 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ì:

[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
                            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 nominare le tue classi come preferisci, l'importante è decorare le tue classi con attributi che corrispondano al tipo di elaborazione che svolgono. (Ma tieni presente che ci sono delle convenzioni di denominazione per le gerarchie di classi in Dynamics AX 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 istanziare oggetti delle sottoclassi in base alle tue esigenze.

Nella tua superclasse (MyProcessor), potresti aggiungere un metodo di costruzione come questo:

public static MyProcessor construct(ModuleInventPurchSales _module,
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. Ciò che fa questo metodo è accettare il nome della superclasse di una gerarchia (e questa superclasse non deve essere in cima alla gerarchia; significa semplicemente che solo le classi che estendono questa classe saranno idonee) 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 convalida o la logica che desideri, ma la cosa importante da ricordare è che una volta implementato, non dovresti più dover modificare questo metodo. Puoi aggiungere sottoclassi alla gerarchia e finché ti assicuri di decorarle in modo appropriato, il metodo di costruzione le troverà anche se non esistevano quando è stato scritto.

E le prestazioni? Onestamente non ho provato a fare un benchmark, ma il mio istinto mi dice che probabilmente funziona peggio del classico design dell'istruzione switch. Tuttavia, considerando che la maggior parte dei problemi di prestazioni in Dynamics AX sono causati dall'accesso al database, non me ne preoccuperei troppo.

Naturalmente, se stai implementando qualcosa che richiederà la creazione rapida di migliaia di oggetti, potresti voler indagare ulteriormente, ma nei casi classici in cui istanzia un singolo oggetto per eseguire un'elaborazione prolungata, dubito che avrà 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 tue sottoclassi anche se sei certo che siano decorate correttamente, potrebbe trattarsi di un problema di memorizzazione nella cache. Prova a cancellare le cache sia sul client che sul server. Non dovrebbe essere necessario riavviare effettivamente l'AOS, ma potrebbe essere l'ultima risorsa.

Condividi su BlueskyCondividi su FacebookCondividi su LinkedInCondividi su TumblrCondividi su XCondividi su LinkedInAggiungi su Pinterest

Mikkel Bang Christensen

Sull'autore

Mikkel Bang Christensen
Mikkel è il creatore e proprietario di miklix.com. Ha oltre 20 anni di esperienza come programmatore di computer/sviluppatore di software ed è attualmente impiegato a tempo pieno in una grande azienda IT europea. Quando non scrive sul blog, dedica il suo tempo libero a una vasta gamma di interessi, hobby e attività, che in qualche modo si riflettono nella varietà di argomenti trattati in questo sito.