Utilizarea cadrului SysExtension pentru a afla ce subclasă să instanțieze în Dynamics AX 2012
Publicat: 16 februarie 2025 la 00:25:56 UTC
Ultima actualizare: 12 ianuarie 2026 la 08:43:13 UTC
Acest articol descrie cum se utilizează framework-ul SysExtension, puțin cunoscut, din Dynamics AX 2012 și Dynamics 365 for Operations pentru a instanția subclase pe baza decorațiunilor de atribute, permițând un design ușor extensibil al unei ierarhii de clase de procesare.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
Informațiile din această postare se bazează pe Dynamics AX 2012 R3. Este posibil să fie sau nu valabile pentru alte versiuni. (Actualizare: Pot confirma că informațiile din acest articol sunt valabile și pentru Dynamics 365 for Operations)
Când implementați clase de procesare în Dynamics AX, vă confruntați adesea cu crearea unei ierarhii de clase în care fiecare subclasă corespunde unei valori enum sau are o altă cuplare de date. Un design clasic este de a avea apoi o metodă de construcție în superclasa, care are un comutator ce determină ce clasă să instanțieze pe baza intrării.
În principiu, acest lucru funcționează bine, dar dacă aveți multe intrări posibile diferite (multe elemente într-o enumerare sau poate intrarea este o combinație de mai multe valori diferite), poate deveni plictisitor și predispus la erori de întreținut, iar designul are întotdeauna dezavantajul că va trebui să modificați metoda de construcție respectivă dacă adăugați vreodată o nouă subclasă sau faceți modificări la ce subclasă ar trebui utilizată în funcție de ce intrare.
Din fericire, există o modalitate mult mai elegantă, dar din păcate și mult mai puțin cunoscută, de a face acest lucru, și anume prin utilizarea framework-ului SysExtension.
Acest framework profită de atributele pe care le puteți folosi pentru a decora subclasele, astfel încât sistemul să poată identifica ce subclasă ar trebui utilizată pentru ce anume gestionează. Veți avea în continuare nevoie de o metodă de construcție, dar dacă este făcută corect, nu va trebui niciodată să o modificați atunci când adăugați noi subclase.
Să luăm un exemplu imaginar și să presupunem că veți implementa o ierarhie care efectuează un fel de procesare bazată pe tabelul InventTrans. Procesarea care trebuie efectuată depinde de StatusReceipt și StatusIssue ale înregistrărilor, precum și de faptul dacă înregistrările sunt legate de SalesLine, PurchLine sau niciuna dintre ele. Deja acum, vă uitați la o mulțime de combinații diferite.
Să presupunem atunci că știi că deocamdată trebuie să gestionezi doar câteva combinații, dar știi și că ți se va cere să poți gestiona din ce în ce mai multe combinații în timp.
Să simplificăm lucrurile relativ și să presupunem că, deocamdată, trebuie doar să gestionați înregistrări legate de SalesLine cu un StatusIssue ReservPhysical sau ReservOrdered, toate celelalte combinații pot fi ignorate deocamdată, dar, din moment ce știți că va trebui să le gestionați mai târziu, veți dori să vă proiectați codul într-un mod care să îl facă ușor extensibil.
Ierarhia ta ar putea arăta cam așa deocamdată:
- ProcesorulMeuVânzăriProcesorulMeuVânzăriProcesorulMeuRezervareComandatRezervare_ProcesorulMeuFizică
Acum, ați putea implementa cu ușurință o metodă în superclasa care instanțiază o subclasă bazată pe un ModuleInventPurchSales și o enumerare StatusIssue. Dar va trebui apoi să modificați superclasa de fiecare dată când adăugați o subclasă, iar aceasta nu este ideea de moștenire în programarea orientată pe obiecte. La urma urmei, nu trebuie să modificați RunBaseBatch sau SysOperationServiceBase de fiecare dată când adăugați un nou job batch.
În schimb, puteți utiliza framework-ul SysExtension. Acest lucru va necesita adăugarea unei alte clase, care trebuie să extindă SysAttribute. Această clasă va fi utilizată ca atribut cu care puteți decora clasele de procesare.
Această clasă este foarte similară cu modul în care ați crea o clasă de contract de date pentru o implementare SysOperation, prin faptul că va avea anumiți membri de date și metode param pentru obținerea și setarea acelor valori.
În cazul nostru, ClassDeclaration ar putea arăta cam așa:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Trebuie să creezi o metodă new() pentru instanțierea tuturor membrilor de date. Dacă dorești, poți da valori implicite unora sau tuturor acestora, dar eu nu am făcut asta.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Și ar trebui să implementați și o metodă parm pentru fiecare element de date, dar le-am omis aici, deoarece sunt sigur că știți cum să faceți asta - altfel, haideți să o considerăm un exercițiu ;-)
Acum puteți utiliza clasa de atribute pentru a decora fiecare dintre clasele de procesare. De exemplu, declarațiile clasei ar putea arăta astfel:
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
{
}
Desigur, puteți denumi clasele cum doriți, însă important este să le decorați cu atribute care corespund tipului de procesare pe care îl efectuează. (Rețineți însă că există convenții de denumire pentru ierarhiile de clase în Dynamics AX și este întotdeauna o idee bună să le respectați, dacă este posibil).
Acum, că ți-ai decorat clasele pentru a identifica ce tip de procesare face fiecare dintre ele, poți profita de framework-ul SysExtension pentru a instanția obiectele subclaselor, după cum este necesar.
În superclasa ta (MyProcessor), ai putea adăuga o metodă de construcție ca aceasta:
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;
}
Partea cu adevărat interesantă - și, de fapt, obiectul (scuzați jocul de cuvinte) al întregii postări - este metoda getClassFromSysAttribute() din clasa SysExtensionAppClassFactory. Ceea ce face această metodă este că acceptă numele superclasei unei ierarhii (și această superclasă nu trebuie să fie în vârful ierarhiei; înseamnă pur și simplu că doar clasele care extind această clasă vor fi eligibile) și un obiect atribut.
Apoi returnează un obiect al unei clase care extinde superclasa specificată și este decorat cu un atribut corespunzător.
Evident, poți adăuga oricâtă validare sau logică suplimentară metodei de construcție dorești, dar important de reținut este că, odată implementată, nu va mai trebui niciodată să modifici această metodă. Poți adăuga subclase în ierarhie și, atâta timp cât te asiguri că le decorezi corespunzător, metoda de construcție le va găsi chiar dacă nu existau atunci când a fost scrisă.
Dar performanța? Sincer, nu am încercat să o testez, dar am impresia că probabil are performanțe mai slabe decât designul clasic al instrucțiunilor switch. Totuși, având în vedere că, de departe, cele mai multe probleme de performanță din Dynamics AX sunt cauzate de accesul la baza de date, nu mi-aș face prea multe griji în privința asta.
Desigur, dacă implementați ceva ce va necesita crearea rapidă a mii de obiecte, este posibil să doriți să investigați mai departe, dar în cazurile clasice în care instanțiați doar un singur obiect pentru a efectua o procesare lungă, mă îndoiesc că va conta. De asemenea, având în vedere sfatul meu de depanare (paragraful următor), se pare că framework-ul SysExtension se bazează pe caching, așa că într-un sistem care rulează mă îndoiesc că are o scădere semnificativă a performanței.
Depanare: Dacă metoda de construcție nu găsește subclasele, chiar dacă sunteți sigur că sunt decorate corect, ar putea fi o problemă de cache. Încercați să goliți cache-urile atât pe client, cât și pe server. Nu ar trebui să fie necesar să reporniți AOS, dar ar putea fi ultima soluție.
Lectură suplimentară
Dacă ți-a plăcut această postare, s-ar putea să-ți placă și aceste sugestii:
- Eroare „Nici o clasă de metadate definită pentru obiectul contractului de date” în Dynamics AX 2012
- Convertiți un real în șir cu toate zecimale în Dynamics AX 2012
- Diferența dintre data() și buf2Buf() în Dynamics AX 2012
