Miklix

Bruke SysExtension Framework for å finne ut hvilken underklasse som skal instansieres i Dynamics AX 2012

Publisert: 16. februar 2025 kl. 00:25:52 UTC
Sist oppdatert: 13. september 2025 kl. 22:52:55 UTC

Denne artikkelen beskriver hvordan du bruker det lite kjente SysExtension-rammeverket i Dynamics AX 2012 og Dynamics 365 for Operations til å starte underklasser basert på attributtdekorasjoner, noe som gjør det enkelt å utvide et behandlingsklassehierarki.


Denne siden er maskinoversatt fra engelsk for å gjøre den tilgjengelig for så mange som mulig. Dessverre er maskinoversettelse ennå ikke en fullkommen teknologi, så det kan forekomme feil. Hvis du foretrekker det, kan du se den engelske originalversjonen her:

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

Informasjonen i dette innlegget er basert på Dynamics AX 2012 R3. Det kan være gyldig for andre versjoner. (Oppdatering: Jeg kan bekrefte at informasjonen i denne artikkelen også er gyldig for Dynamics 365 for Operations)

Når du implementerer behandlingsklasser i Dynamics AX, står du ofte overfor å opprette et klassehierarki der hver underklasse tilsvarer en opplistingsverdi eller har en annen datakobling. Et klassisk design er da å ha en konstruksjonsmetode i superklassen, som har en bryter som bestemmer hvilken klasse som skal instansieres basert på inngangen.

Dette fungerer bra i prinsippet, men hvis du har mange forskjellige mulige innganger (mange elementer i en enum eller kanskje inngangen er en kombinasjon av flere forskjellige verdier), kan det bli kjedelig og feilutsatt å vedlikeholde, og designet har alltid den ulempen at du må endre nevnte konstruksjonsmetode hvis du noen gang legger til en ny underklasse eller gjør endringer i hvilken underklasse som skal brukes basert på hvilke innganger.

Heldigvis finnes det en mye mer elegant, men dessverre også mye mindre kjent, måte å gjøre dette på, nemlig ved bruk av SysExtension-rammeverket.

Dette rammeverket utnytter attributter som du kan bruke til å dekorere underklassene dine for å gjøre systemet i stand til å finne ut hvilken underklasse som skal brukes til å håndtere hva. Du trenger fortsatt en konstruksjonsmetode, men hvis det gjøres riktig, trenger du aldri å endre den når du legger til nye underklasser.

La oss se på et tenkt eksempel og si at du skal implementere et hierarki som gjør en slags behandling basert på InventTrans-tabellen. Hvilken behandling som skal utføres, avhenger av StatusReceipt og StatusIssue for postene, i tillegg til om postene er knyttet til SalesLine, PurchLine eller ingen av delene. Allerede nå ser du på mange forskjellige kombinasjoner.

La oss da si at du vet at foreløpig trenger du bare å håndtere en håndfull av kombinasjonene, men du vet også at du vil bli bedt om å kunne håndtere flere og flere kombinasjoner over tid.

La oss holde det relativt enkelt og si at foreløpig trenger du bare å håndtere poster relatert til SalesLine med en StatusIssue av ReservPhysical eller ReservOrdered, alle andre kombinasjoner kan ignoreres foreløpig, men siden du vet at du må håndtere dem senere, vil du utforme koden på en måte som gjør den lett utvidbar.

Hierarkiet ditt kan se omtrent slik ut foreløpig:

  • Min prosessor
    • MyProcessor_Sales
      • MyProcessor_Sales_ReservOrdered
      • MyProcessor_Sales_ReservPhysical

Nå kan du enkelt implementere en metode i superklassen som instansierer en underklasse basert på en ModuleInventPurchSales og en StatusIssue-enum. Men du må da endre superklassen hver gang du legger til en underklasse, og det er egentlig ikke ideen om arv i objektorientert programmering. Tross alt trenger du ikke å endre RunBaseBatch eller SysOperationServiceBase hver gang du legger til en ny satsvis jobb.

I stedet kan du bruke SysExtension-rammeverket. Det vil kreve at du legger til en annen klasse, som må utvide SysAttribute. Denne klassen vil bli brukt som attributtet du kan dekorere behandlingsklassene dine med.

Denne klassen er veldig lik hvordan du ville laget en datakontraktklasse for en SysOperation-implementering, ved at den vil ha noen datamedlemmer og parm-metoder for å hente og angi disse verdiene.

I vårt tilfelle kan ClassDeclaration se omtrent slik ut:

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

Du må lage en new() metode for å instansiere alle datamedlemmer. Hvis du ønsker det, kan du gi noen eller alle standardverdier, men det har jeg ikke gjort.

public void new(ModuleInventPurchSales  _module,
                StatusIssue             _statusIssue,
                StatusReceipt           _statusReceipt)
{
    ;

    super();

    module          = _module;
    statusIssue     = _statusIssue;
    statusReceipt   = _statusReceipt;
}

Og du bør også implementere en parm-metode for hvert datamedlem, men jeg har utelatt dem her, da jeg er sikker på at du vet hvordan du gjør det - ellers, la oss betrakte det som en øvelse ;-)

Nå kan du bruke attributtklassen din til å dekorere hver av behandlingsklassene dine. Klasseerklæringene kan for eksempel se slik ut:

[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
{
}

Du kan selvfølgelig navngi klassene dine som du vil, den viktige delen her er at du dekorerer klassene dine med attributter som samsvarer med hva slags prosessering de gjør. (Men husk at det er navnekonvensjoner for klassehierarkier i Dynamics AX, og det er alltid en god idé å følge disse, hvis mulig).

Nå som du har dekorert klassene dine for å identifisere hva slags behandling hver av dem gjør, kan du dra nytte av SysExtension-rammeverket for å instansiere objekter i underklassene etter behov.

I superklassen din (MyProcessor) kan du legge til en konstruksjonsmetode som denne:

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;
}

Den virkelig interessante delen - og egentlig objektet (unnskyld ordspillet) i hele dette innlegget - er getClassFromSysAttribute()-metoden i SysExtensionAppClassFactory-klassen. Det denne metoden gjør er at den aksepterer navnet på superklassen i et hierarki (og denne superklassen trenger ikke å være øverst i hierarkiet; det betyr ganske enkelt at bare klasser som utvider denne klassen vil være kvalifisert) og et attributtobjekt.

Den returnerer deretter et objekt i en klasse som utvider den angitte superklassen og er dekorert med et tilsvarende attributt.

Du kan åpenbart legge til så mye mer validering eller logikk til konstruksjonsmetoden som du ønsker, men det viktige å ta med seg her er at når den først er implementert, bør du aldri måtte endre denne metoden igjen. Du kan legge til underklasser i hierarkiet, og så lenge du sørger for å dekorere dem på riktig måte, vil konstruksjonsmetoden finne dem selv om de ikke eksisterte da den ble skrevet.

Hva med ytelse? Jeg har ærlig talt ikke forsøkt å måle det, men magefølelsen min er at dette sannsynligvis yter dårligere enn det klassiske bryterutsagnsdesignet. Men med tanke på at de desidert fleste ytelsesproblemene i Dynamics AX er forårsaket av databasetilgang, ville jeg ikke bekymre meg for mye om det.

Selvfølgelig, hvis du implementerer noe som vil kreve at tusenvis av objekter opprettes raskt, kan det være lurt å undersøke nærmere, men i de klassiske tilfellene der du bare instantierer et enkelt objekt for å gjøre litt langvarig behandling, tviler jeg på at det vil ha noe å si. Med tanke på feilsøkingstipset mitt (neste avsnitt), ser det ut til at SysExtension-rammeverket er avhengig av caching, så i et kjørende system tviler jeg på at det har et betydelig ytelsestreff.

Feilsøking: Hvis konstruksjonsmetoden ikke finner underklassene dine selv om du er sikker på at de er dekorert riktig, kan det være et caching-problem. Prøv å tømme hurtigbuffere på både klient og server. Det burde ikke være nødvendig å faktisk starte AOS på nytt, men det kan være siste utvei.

Videre lesing

Hvis du likte dette innlegget, kan du også like disse forslagene:


Del på BlueskyDel på FacebookDel på LinkedInDel på TumblrDel på XDel på LinkedInFest på Pinterest

Mikkel Christensen

Om forfatteren

Mikkel Christensen
Mikkel er skaperen og eieren av miklix.com. Han har over 20 års erfaring som profesjonell dataprogrammerer/programvareutvikler og er for tiden ansatt på fulltid i et stort europeisk IT-selskap. Når han ikke blogger, bruker han fritiden sin på en lang rekke interesser, hobbyer og aktiviteter, noe som til en viss grad kan gjenspeiles i de mange ulike temaene som dekkes på dette nettstedet.