Utilizarea cadrului SysExtension pentru a afla ce subclasă să instanțieze în Dynamics AX 2012
Publicat: 16 februarie 2025 la 00:25:56 UTC
Acest articol descrie cum să utilizați cadrul puțin cunoscut SysExtension din Dynamics AX 2012 și Dynamics 365 for Operations pentru a instanția subclase bazate pe decorațiuni de atribute, permițând un proiect ușor extensibil al unei ierarhii a claselor 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. Poate fi valabil sau nu 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 clasă în care fiecare subclasă corespunde unei valori enumerate sau are o altă cuplare de date. Un design clasic este de a avea apoi o metodă de construcție în super-clasa, care are un comutator care determină ce clasă să instanțieze pe baza intrării.
Acest lucru funcționează bine în principiu, dar dacă aveți multe intrări posibile diferite (multe elemente într-o enumerare sau poate că intrarea este o combinație de mai multe valori diferite), poate deveni obositor și predispus la erori de întreținut, iar designul are întotdeauna dezavantajul că va trebui să modificați metoda de construcție dacă adăugați vreodată o nouă subclasă sau faceți modificări la care subclasă ar trebui utilizată în funcție de 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 cadru profită de atributele pe care le puteți folosi pentru a vă decora subclasele pentru a face sistemul să poată da seama ce subclasă ar trebui utilizată pentru a gestiona ce. 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ă ne uităm la un exemplu imaginar și să spunem că veți implementa o ierarhie care face un fel de procesare bazată pe tabelul InventTrans. Ce procesare de făcut depinde de StatusReceipt și StatusIssue ale înregistrărilor, precum și de dacă înregistrările sunt legate de SalesLine, PurchLine sau niciunul. Deja acum, te uiți la o mulțime de combinații diferite.
Să spunem atunci că știi că deocamdată trebuie să te ocupi doar de o mână de 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ă rămânem relativ simplu și să spunem că deocamdată trebuie să gestionați doar înregistrările legate de SalesLine cu o problemă de stare 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 poate arăta cam așa deocamdată:
- MyProcessor
- MyProcessor_Sales
- MyProcessor_Sales_ReservOrdered
- MyProcessor_Sales_ReservPhysical
- MyProcessor_Sales
Acum, puteți implementa cu ușurință o metodă în super-clasă care instanțiază o subclasă bazată pe o enumerare ModuleInventPurchSales și StatusIssue. Dar va trebui apoi să modificați superclasa de fiecare dată când adăugați o subclasă și aceasta nu este chiar 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 folosi cadrul SysExtension. Acest lucru va necesita să adăugați o altă clasă, care trebuie să extindă SysAttribute. Această clasă va fi folosită ca atribut cu care vă puteți decora clasele de procesare.
Această clasă este foarte similară cu modul în care ați face o clasă de contract de date pentru o implementare SysOperation, prin faptul că va avea câțiva membri de date și metode parm pentru obținerea și setarea acestor valori.
În cazul nostru, ClassDeclaration poate arăta cam așa:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Trebuie să creați o metodă new() pentru instanțierea tuturor membrilor datelor. Dacă doriți, puteți da unele sau toate valorile implicite, dar nu am făcut asta.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Și ar trebui să implementați, de asemenea, o metodă parm pentru fiecare membru de date, dar le-am omis aici, deoarece sunt sigur că știți cum să faceți asta - altfel, să considerăm că este un exercițiu ;-)
Acum puteți folosi clasa dvs. de atribut pentru a decora fiecare dintre clasele dvs. de procesare. De exemplu, declarațiile de clasă 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, vă puteți denumi clasele cum doriți, partea importantă aici este că vă decorați clasele cu atribute care corespund tipului de procesare pe care o fac. (Dar rețineți că există convenții de denumire pentru ierarhiile de clasă în Dynamics AX și este întotdeauna o idee bună să le urmați, dacă este posibil).
Acum că v-ați decorat clasele pentru a identifica ce fel de procesare face fiecare dintre ele, puteți profita de cadrul SysExtension pentru a instanția obiecte ale subclaselor după cum este necesar.
În super-clasa (MyProcessor), puteți 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 chiar obiectul (iertați jocul de cuvinte) al întregului post - este metoda getClassFromSysAttribute() din clasa SysExtensionAppClassFactory. Ceea ce face această metodă este că acceptă numele superclasei unei ierarhii (și această super clasă nu trebuie să fie în vârful ierarhiei; înseamnă pur și simplu că numai 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.
Puteți adăuga, evident, cât mai multă validare sau logică la metoda de construcție doriți, dar concluzia importantă aici este că, odată implementată, nu ar trebui să trebuiască să modificați din nou această metodă. Puteți adăuga subclase la ierarhie și atâta timp cât vă asigurați că le decorați corespunzător, metoda de construcție le va găsi chiar dacă nu existau când a fost scrisă.
Ce zici de performanță? Sincer, nu am încercat să-l evaluez, dar sentimentul meu este că acest lucru probabil funcționează mai rău decât designul clasic al switch-ului. Cu toate acestea, 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-ar face griji prea mult pentru asta.
Desigur, dacă implementați ceva care va necesita crearea rapidă a mii de obiecte, poate doriți să investigați mai departe, dar în cazurile clasice în care instanțiați un singur obiect pentru a face o procesare îndelungată, mă îndoiesc că va conta. De asemenea, ținând cont de sfatul meu de depanare (paragraful următor), se pare că cadrul SysExtension se bazează pe stocarea în cache, așa că într-un sistem care rulează mă îndoiesc că are o performanță semnificativă. Depanare: dacă metoda de construcție nu găsește subclasele tale, chiar dacă ești sigur că sunt decorate corect, poate fi o problemă de cache. Încercați să ștergeți memoria cache atât pe client, cât și pe server. Nu ar trebui să fie necesar să reporniți efectiv AOS, dar poate fi ultima soluție.