Ús del marc SysExtension per esbrinar quina subclasse s'ha d'instanciar al Dynamics AX 2012
Publicat: 5 de març del 2025, a les 19:30:27 UTC
Aquest article descriu com utilitzar el marc SysExtension poc conegut al Dynamics AX 2012 i al Dynamics 365 for Operations per crear subclasses basades en decoracions d'atributs, cosa que permet un disseny fàcilment extensible d'una jerarquia de classes de processament.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
La informació d'aquesta publicació es basa en Dynamics AX 2012 R3. Pot ser vàlid o no per a altres versions. (Actualització: puc confirmar que la informació d'aquest article també és vàlida per al Dynamics 365 for Operations)
Quan implementeu classes de processament al Dynamics AX, sovint us enfronteu a la creació d'una jerarquia de classes en la qual cada subclasse correspon a un valor enumerador o té algun altre acoblament de dades. Un disseny clàssic és tenir un mètode de construcció a la superclasse, que té un interruptor que determina quina classe s'ha d'instanciar en funció de l'entrada.
Això funciona bé en principi, però si teniu moltes entrades possibles diferents (molts elements en una enumeració o potser l'entrada és una combinació de diversos valors diferents), pot ser tediós i propens a errors de mantenir i el disseny sempre té l'inconvenient que haureu de modificar aquest mètode de construcció si mai afegiu una nova subclasse o feu canvis a quina subclasse s'ha d'utilitzar en funció de quina entrada.
Afortunadament, hi ha una manera molt més elegant, però malauradament també molt menys coneguda, de fer-ho, és a dir, mitjançant l'ús del marc SysExtension.
Aquest marc aprofita els atributs que podeu utilitzar per decorar les vostres subclasses perquè el sistema pugui esbrinar quina subclasse s'ha d'utilitzar per gestionar què. Encara necessitareu un mètode de construcció, però si es fa correctament, mai no haureu de modificar-lo quan afegiu subclasses noves.
Vegem un exemple imaginari i diguem que implementareu una jerarquia que fa algun tipus de processament basat en la taula InventTrans. El processament que cal fer depèn de l'estatusReceipt i StatusIssue dels registres, així com de si els registres estan relacionats amb SalesLine, PurchLine o cap dels dos. Ara ja esteu mirant moltes combinacions diferents.
Diguem aleshores que saps que ara per ara només cal manejar un grapat de combinacions, però també saps que se't demanarà que puguis gestionar més i més combinacions amb el temps.
Siguem-ho relativament senzill i diguem que de moment només necessiteu gestionar els registres relacionats amb SalesLine amb un StatusIssue de ReservPhysical o ReservOrdered, totes les altres combinacions es poden ignorar de moment, però com que sabeu que les haureu de gestionar més tard, voldreu dissenyar el vostre codi d'una manera que sigui fàcilment extensible.
La vostra jerarquia pot semblar ara com aquesta:
- El meu processador
- El meu processador_vendes
- MyProcessor_Sales_ReservOrdered
- MyProcessor_Sales_ReservPhysical
- El meu processador_vendes
Ara, podeu implementar fàcilment un mètode a la superclasse que instància una subclasse basada en un ModuleInventPurchSales i una enumeració StatusIssue. Però llavors haureu de modificar la superclasse cada vegada que afegiu una subclasse, i aquesta no és realment la idea de l'herència en la programació orientada a objectes. Després de tot, no cal que modifiqueu RunBaseBatch o SysOperationServiceBase cada vegada que afegiu un treball per lots nou.
En lloc d'això, podeu fer ús del marc SysExtension. Això requerirà que afegiu una altra classe, que ha d'estendre SysAttribute. Aquesta classe s'utilitzarà com a atribut amb què podeu decorar les vostres classes de processament.
Aquesta classe és molt semblant a com crearíeu una classe de contracte de dades per a una implementació de SysOperation, ja que tindrà alguns membres de dades i mètodes parm per obtenir i establir aquests valors.
En el nostre cas, la Declaració de classe pot semblar a això:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Heu de crear un mètode new() per crear una instancia de tots els membres de dades. Si voleu, podeu donar-los alguns o tots els valors per defecte, però no ho he fet.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
I també hauríeu d'implementar un mètode parm per a cada membre de dades, però els he omès aquí, ja que estic segur que sabeu com fer-ho; en cas contrari, considerem-ho un exercici ;-)
Ara podeu utilitzar la vostra classe d'atributs per decorar cadascuna de les vostres classes de processament. Per exemple, les declaracions de classe podrien ser així:
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
{
}
Per descomptat, podeu anomenar les vostres classes de la manera que vulgueu, la part important aquí és que decoreu les vostres classes amb atributs que corresponguin al tipus de processament que fan. (Però tingueu en compte que hi ha convencions de nomenclatura per a les jerarquies de classes a Dynamics AX i sempre és una bona idea seguir-les, si és possible).
Ara que heu decorat les vostres classes per identificar quin tipus de processament fa cadascuna d'elles, podeu aprofitar el marc SysExtension per crear una instancia d'objectes de les subclasses segons sigui necessari.
A la vostra superclasse (MyProcessor), podeu afegir un mètode de construcció com aquest:
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 part realment interessant, i realment l'objecte (perdoneu el joc de paraules) de tota aquesta publicació, és el mètode getClassFromSysAttribute() de la classe SysExtensionAppClassFactory. El que fa aquest mètode és que accepta el nom de la superclasse d'una jerarquia (i aquesta superclasse no ha d'estar a la part superior de la jerarquia; simplement vol dir que només seran elegibles les classes que estenguin aquesta classe) i un objecte d'atribut.
A continuació, retorna un objecte d'una classe que amplia la superclasse especificada i està decorat amb un atribut corresponent.
Òbviament, podeu afegir tanta validació o lògica al mètode de construcció com vulgueu, però l'important és que un cop implementat, no hauríeu de tornar a modificar aquest mètode. Podeu afegir subclasses a la jerarquia i, sempre que us assegureu de decorar-les adequadament, el mètode de construcció les trobarà tot i que no existien quan es va escriure.
Què passa amb el rendiment? Sincerament, no he intentat comparar-lo, però la meva intenció és que això probablement funciona pitjor que el disseny clàssic de declaració de commutació. Tanmateix, tenint en compte que la majoria dels problemes de rendiment a Dynamics AX són causats per l'accés a la base de dades, no em preocuparia massa per això.
Per descomptat, si esteu implementant alguna cosa que requerirà que es creïn milers d'objectes ràpidament, potser voldreu investigar més, però en els casos clàssics en què només instancieu un únic objecte per fer un processament llarg, dubto que tingui importància. A més, tenint en compte el meu consell de resolució de problemes (següent paràgraf), sembla que el marc de SysExtension es basa en la memòria cau, de manera que en un sistema en execució dubto que tingui un èxit de rendiment important. Proveu d'esborrar la memòria cau tant al client com al servidor. No hauria de ser necessari reiniciar l'AOS, però pot ser l'últim recurs.