Använda SysExtension Framework för att ta reda på vilken underklass som ska instansieras i Dynamics AX 2012
Publicerad: 16 februari 2025 kl. 00:26:00 UTC
Senast uppdaterad: 12 januari 2026 kl. 08:43:18 UTC
Den här artikeln beskriver hur man använder det mindre kända SysExtension-ramverket i Dynamics AX 2012 och Dynamics 365 for Operations för att instansiera underklasser baserat på attributdekorationer, vilket möjliggör en lätt utökningsbar design av en bearbetningsklasshierarki.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
Informationen i det här inlägget är baserad på Dynamics AX 2012 R3. Den kan vara giltig för andra versioner, men inte för andra versioner. (Uppdatering: Jag kan bekräfta att informationen i den här artikeln även gäller för Dynamics 365 for Operations)
När man implementerar bearbetningsklasser i Dynamics AX ställs man ofta inför att skapa en klasshierarki där varje underklass motsvarar ett enum-värde eller har någon annan datakoppling. En klassisk design är att sedan ha en konstruktionsmetod i superklassen, vilken har en switch som avgör vilken klass som ska instansieras baserat på indata.
Detta fungerar bra i princip, men om du har många olika möjliga indata (många element i en enum eller kanske indatan är en kombination av flera olika värden), kan det bli mödosamt och felbenäget att underhålla och designen har alltid nackdelen att du kommer att behöva modifiera nämnda konstruktionsmetod om du någonsin lägger till en ny underklass eller gör ändringar i vilken underklass som ska användas baserat på vilken indata.
Lyckligtvis finns det ett mycket mer elegant, men tyvärr också mycket mindre känt, sätt att göra detta, nämligen genom att använda SysExtension-ramverket.
Detta ramverk utnyttjar attribut som du kan använda för att dekorera dina underklasser så att systemet kan avgöra vilken underklass som ska användas för att hantera vad. Du kommer fortfarande att behöva en konstruktionsmetod, men om den görs korrekt behöver du aldrig ändra den när du lägger till nya underklasser.
Låt oss titta på ett tänkt exempel och säga att du ska implementera en hierarki som utför någon form av bearbetning baserat på InventTrans-tabellen. Vilken bearbetning som ska göras beror på StatusReceipt och StatusIssue för posterna, samt om posterna är relaterade till SalesLine, PurchLine eller ingendera. Redan nu tittar du på många olika kombinationer.
Låt oss då säga att du vet att du för tillfället bara behöver hantera en handfull av kombinationerna, men du vet också att du kommer att bli ombedd att kunna hantera fler och fler kombinationer över tid.
Låt oss hålla det relativt enkelt och säga att för tillfället behöver du bara hantera poster relaterade till SalesLine med StatusIssue på ReservPhysical eller ReservOrdered. Alla andra kombinationer kan ignoreras för tillfället, men eftersom du vet att du kommer att behöva hantera dem senare, bör du utforma din kod på ett sätt som gör den lätt utökningsbar.
Din hierarki kan se ut ungefär så här för tillfället:
- MinProcessorMinProcessor_FörsäljningMinProcessor_Försäljning_ReservBeställdMinProcessor_Försäljning_ReservFysisk
Nu skulle du enkelt kunna implementera en metod i superklassen som instansierar en underklass baserad på en ModuleInventPurchSales och en StatusIssue-enum. Men du kommer då att behöva ändra superklassen varje gång du lägger till en underklass, och det är inte riktigt idén med arv i objektorienterad programmering. Du behöver trots allt inte ändra RunBaseBatch eller SysOperationServiceBase varje gång du lägger till ett nytt batchjobb.
Istället kan du använda SysExtension-ramverket. Det kräver att du lägger till ytterligare en klass, som behöver utöka SysAttribute. Denna klass kommer att användas som attributet du kan dekorera dina bearbetningsklasser med.
Den här klassen är väldigt lik hur du skulle skapa en datakontraktsklass för en SysOperation-implementering, eftersom den kommer att ha några datamedlemmar och parm-metoder för att hämta och ställa in dessa värden.
I vårt fall kan ClassDeclaration se ut ungefär så här:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Du behöver skapa en new()-metod för att instansiera alla dataelement. Om du vill kan du ge standardvärden till vissa eller alla, men det har jag inte gjort.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Och du bör också implementera en parm-metod för varje datamedlem, men jag har utelämnat dem här eftersom jag är säker på att du vet hur man gör det - annars kan vi betrakta det som en övning ;-)
Nu kan du använda din attributklass för att dekorera var och en av dina bearbetningsklasser. Till exempel kan klassdeklarationerna se ut så här:
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 naturligtvis namnge dina klasser hur du vill, det viktiga här är att du dekorerar dina klasser med attribut som motsvarar vilken typ av bearbetning de utför. (Men tänk på att det finns namngivningskonventioner för klasshierarkier i Dynamics AX och det är alltid en bra idé att följa dem, om möjligt).
Nu när du har dekorerat dina klasser för att identifiera vilken typ av bearbetning var och en av dem utför, kan du dra nytta av SysExtension-ramverket för att instansiera objekt i underklasserna efter behov.
I din superklass (MyProcessor) kan du lägga till en konstruktionsmetod som denna:
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 verkligt intressanta delen – och egentligen objektet (ursäkta ordvitsen) med hela det här inlägget – är metoden getClassFromSysAttribute() i klassen SysExtensionAppClassFactory. Metoden accepterar namnet på superklassen i en hierarki (och denna superklass behöver inte vara högst upp i hierarkin; det betyder helt enkelt att endast klasser som utökar denna klass kommer att vara berättigade) och ett attributobjekt.
Den returnerar sedan ett objekt i en klass som utökar den angivna superklassen och dekoreras med ett motsvarande attribut.
Du kan givetvis lägga till så mycket ytterligare validering eller logik till konstruktionsmetoden som du vill, men den viktiga slutsatsen här är att när den väl är implementerad ska du aldrig behöva ändra metoden igen. Du kan lägga till underklasser i hierarkin och så länge du ser till att dekorera dem på lämpligt sätt kommer konstruktionsmetoden att hitta dem även om de inte existerade när den skrevs.
Hur är det med prestanda? Jag har ärligt talat inte försökt att jämföra det, men min magkänsla säger att det här förmodligen presterar sämre än den klassiska switch-satsdesignen. Men med tanke på att de flesta prestandaproblemen i Dynamics AX orsakas av databasåtkomst, skulle jag inte oroa mig så mycket för det.
Om du implementerar något som kräver att tusentals objekt skapas snabbt kanske du vill undersöka saken närmare, men i de klassiska fallen där du bara instansierar ett enda objekt för att utföra en längre bearbetning tvivlar jag på att det spelar någon roll. Med tanke på mitt felsökningstips (nästa stycke) verkar det också som att SysExtension-ramverket är beroende av cachning, så i ett körande system tvivlar jag på att det har en betydande prestandaförlust.
Felsökning: Om construct-metoden inte hittar dina underklasser trots att du är säker på att de är korrekt dekorerade, kan det vara ett cachproblem. Försök att rensa cacheminnen på både klienten och servern. Det borde inte vara nödvändigt att starta om AOS, men det kan vara en sista utväg.
Vidare läsning
Om du gillade det här inlägget kanske du också gillar dessa förslag:
- Strängformatering med makro och strFmt i Dynamics AX 2012
- Konvertera en reell till sträng med alla decimaler i Dynamics AX 2012
- Dynamics AX 2012 SysOperation Framework Snabböversikt
