Het SysExtension Framework gebruiken om erachter te komen welke subklasse u moet instantiëren in Dynamics AX 2012
Gepubliceerd: 16 februari 2025 om 00:25:53 UTC
Laatst bijgewerkt: 12 januari 2026 om 08:43:07 UTC
Dit artikel beschrijft hoe u het relatief onbekende SysExtension-framework in Dynamics AX 2012 en Dynamics 365 for Operations kunt gebruiken om subklassen te instantiëren op basis van attribuutdecoraties, waardoor een eenvoudig uitbreidbaar ontwerp van een verwerkingsklassehiërarchie mogelijk wordt.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
De informatie in dit bericht is gebaseerd op Dynamics AX 2012 R3. Deze informatie is mogelijk niet geldig voor andere versies. (Update: Ik kan bevestigen dat de informatie in dit artikel ook geldig is voor Dynamics 365 for Operations.)
Bij het implementeren van verwerkingsklassen in Dynamics AX krijg je vaak te maken met het creëren van een klassenhiërarchie waarin elke subklasse overeenkomt met een enum-waarde of een andere gegevenskoppeling heeft. Een klassiek ontwerp is om vervolgens een constructiemethode in de superklasse te hebben met een schakelaar die bepaalt welke klasse moet worden geïnstantieerd op basis van de invoer.
Dit werkt in principe goed, maar als je veel verschillende mogelijke invoerwaarden hebt (veel elementen in een enum of misschien is de invoer een combinatie van verschillende waarden), kan het onderhoud omslachtig en foutgevoelig worden. Bovendien heeft het ontwerp altijd als nadeel dat je de betreffende constructiemethode moet aanpassen als je ooit een nieuwe subklasse toevoegt of wijzigingen aanbrengt in welke subklasse moet worden gebruikt op basis van welke invoer.
Gelukkig bestaat er een veel elegantere, maar helaas ook veel minder bekende manier om dit te doen, namelijk door gebruik te maken van het SysExtension-framework.
Dit framework maakt gebruik van attributen waarmee je je subklassen kunt decoreren, zodat het systeem zelf kan bepalen welke subklasse voor welke taak gebruikt moet worden. Je hebt nog steeds een constructor nodig, maar als je het correct doet, hoef je die nooit aan te passen wanneer je nieuwe subklassen toevoegt.
Laten we een hypothetisch voorbeeld bekijken. Stel dat u een hiërarchie wilt implementeren die een bepaalde verwerking uitvoert op basis van de tabel InventTrans. Welke verwerking moet worden uitgevoerd, hangt af van de StatusReceipt en StatusIssue van de records, en ook of de records gerelateerd zijn aan SalesLine, PurchLine of geen van beide. Nu ziet u al een groot aantal verschillende combinaties.
Stel dat je weet dat je voorlopig slechts een handvol combinaties hoeft te beheersen, maar dat je ook weet dat er in de loop der tijd steeds meer combinaties van je verwacht zullen worden.
Laten we het relatief eenvoudig houden en ervan uitgaan dat u voorlopig alleen records hoeft te verwerken die gerelateerd zijn aan SalesLine met een StatusIssue van ReservPhysical of ReservOrdered. Alle andere combinaties kunnen voorlopig worden genegeerd, maar aangezien u weet dat u ze later wel zult moeten verwerken, is het belangrijk uw code zo te ontwerpen dat deze gemakkelijk uitbreidbaar is.
Je hiërarchie zou er voorlopig ongeveer zo uit kunnen zien:
- MijnProcessorMijnProcessor_VerkoopMijnProcessor_Verkoop_GereserveerdMijnProcessor_Verkoop_GereserveerdFysiek
Je zou nu eenvoudig een methode in de superklasse kunnen implementeren die een subklasse instantieert op basis van een ModuleInventPurchSales en een StatusIssue enum. Maar dan moet je de superklasse telkens aanpassen wanneer je een subklasse toevoegt, en dat is niet echt de bedoeling van overerving in objectgeoriënteerd programmeren. Je hoeft immers ook niet RunBaseBatch of SysOperationServiceBase aan te passen telkens wanneer je een nieuwe batchtaak toevoegt.
In plaats daarvan kunt u gebruikmaken van het SysExtension-framework. Hiervoor moet u een extra klasse toevoegen die SysAttribute moet uitbreiden. Deze klasse wordt gebruikt als attribuut waarmee u uw verwerkingsklassen kunt decoreren.
Deze klasse is zeer vergelijkbaar met hoe je een datacontractklasse zou maken voor een SysOperation-implementatie, in die zin dat ze een aantal dataleden en parm-methoden bevat voor het ophalen en instellen van die waarden.
In ons geval kan de klassedeclaratie er ongeveer zo uitzien:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Je moet een `new()`-methode maken om alle dataleden te instantiëren. Je kunt er eventueel voor kiezen om sommige of alle dataleden een standaardwaarde te geven, maar dat heb ik zelf niet gedaan.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Je moet ook een `parm`-methode implementeren voor elk data-element, maar die heb ik hier weggelaten omdat ik er zeker van ben dat je dat wel weet - anders beschouw ik het maar als een oefening ;-)
Nu kunt u uw attribuutklasse gebruiken om elk van uw verwerkingsklassen te decoreren. De klassedeclaraties zouden er bijvoorbeeld als volgt uit kunnen zien:
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
{
}
Je kunt je klassen natuurlijk elke gewenste naam geven, het belangrijkste is dat je je klassen voorziet van attributen die overeenkomen met het type verwerking dat ze uitvoeren. (Houd er wel rekening mee dat er naamgevingsconventies zijn voor klassehiërarchieën in Dynamics AX en het is altijd verstandig om die, indien mogelijk, te volgen).
Nu je je klassen hebt voorzien van labels om aan te geven welk type verwerking elk van hen uitvoert, kun je gebruikmaken van het SysExtension-framework om objecten van de subklassen te instantiëren wanneer dat nodig is.
In je superklasse (MyProcessor) kun je een constructiemethode toevoegen zoals deze:
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;
}
Het meest interessante deel - en eigenlijk het doel (om een woordspeling te maken) van dit hele bericht - is de getClassFromSysAttribute()-methode in de SysExtensionAppClassFactory-klasse. Deze methode accepteert de naam van de superklasse van een hiërarchie (en deze superklasse hoeft niet bovenaan de hiërarchie te staan; het betekent simpelweg dat alleen klassen die van deze klasse erven in aanmerking komen) en een attribuutobject.
Vervolgens retourneert de functie een object van een klasse die de opgegeven superklasse uitbreidt en is voorzien van een overeenkomend attribuut.
Je kunt uiteraard zoveel extra validatie of logica aan de `construct`-methode toevoegen als je wilt, maar het belangrijkste is dat je deze methode, eenmaal geïmplementeerd, nooit meer hoeft aan te passen. Je kunt subklassen aan de hiërarchie toevoegen en zolang je ervoor zorgt dat je ze op de juiste manier decoreert, zal de `construct`-methode ze vinden, zelfs als ze nog niet bestonden toen de methode werd geschreven.
En hoe zit het met de prestaties? Ik heb eerlijk gezegd geen benchmark uitgevoerd, maar mijn gevoel zegt dat dit waarschijnlijk slechter presteert dan het klassieke switch-statement. Aangezien de meeste prestatieproblemen in Dynamics AX echter worden veroorzaakt door database-toegang, zou ik me daar niet al te veel zorgen over maken.
Natuurlijk, als je iets implementeert waarbij duizenden objecten snel aangemaakt moeten worden, wil je dit misschien verder onderzoeken, maar in de klassieke gevallen waarin je slechts één object instantieert voor een langdurige verwerking, betwijfel ik of het veel uitmaakt. Bovendien, gezien mijn tip voor probleemoplossing (volgende alinea), lijkt het erop dat het SysExtension-framework gebruikmaakt van caching, dus in een draaiend systeem betwijfel ik of het een significante impact heeft op de prestaties.
Probleemoplossing: Als de `construct`-methode uw subklassen niet vindt, ook al weet u zeker dat ze correct zijn gedecoreerd, kan er sprake zijn van een cacheprobleem. Probeer de caches aan zowel de client- als de serverzijde te wissen. Het is normaal gesproken niet nodig om de AOS opnieuw op te starten, maar het kan een laatste redmiddel zijn.
Verder lezen
Als je dit bericht leuk vond, vind je deze suggesties misschien ook interessant:
- Een rechtspersoon (bedrijfsrekeningen) verwijderen in Dynamics AX 2012
- Converteer een reëel getal naar een tekenreeks met alleen decimalen in Dynamics AX 2012
- Hoe u over de elementen van een enum kunt itereren vanuit X++-code in Dynamics AX 2012
