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.
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
- MyProcessor_Sales
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:
{
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.
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:
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:
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:
- Hvordan iterere over elementene til en Enum fra X++-kode i Dynamics AX 2012
- Strengeformatering med makro og strFmt i Dynamics AX 2012
- Konverter en ekte til streng med alle desimaler i Dynamics AX 2012