Miklix

Brug af SysExtension Framework til at finde ud af, hvilken underklasse der skal instantieres i Dynamics AX 2012

Udgivet: 16. februar 2025 kl. 00.25.36 UTC

Denne artikel beskriver, hvordan man bruger den lidet kendte SysExtension-ramme i Dynamics AX 2012 og Dynamics 365 for Operations til at instansiere underklasser baseret på attributdekorationer, hvilket muliggør et let udvidelsesdesign af et behandlingsklassehierarki.


Denne side er blevet maskinoversat fra engelsk for at gøre den tilgængelig for så mange mennesker som muligt. Desværre er maskinoversættelse endnu ikke en perfekt teknologi, så der kan forekomme fejl. Hvis du foretrækker det, kan du se den originale engelske version her:

Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012

Oplysningerne i dette indlæg er baseret på Dynamics AX 2012 R3. Det er muligvis ikke gyldigt for andre versioner. (Opdatering: Jeg kan bekræfte, at oplysningerne i denne artikel også er gyldige for Dynamics 365 for Operations)

Når du implementerer behandlingsklasser i Dynamics AX, står du ofte over for at skabe et klassehierarki, hvor hver underklasse svarer til en enum-værdi eller har en anden datakobling. Et klassisk design er så at have en konstruktionsmetode i superklassen, som har en switch, der bestemmer hvilken klasse der skal instansieres ud fra inputtet.

Dette fungerer i princippet godt, men hvis du har mange forskellige mulige input (mange elementer i en enum eller måske er input en kombination af flere forskellige værdier), kan det blive trættende og fejlbehæftede at vedligeholde, og designet har altid den ulempe, at du bliver nødt til at modificere nævnte konstruktionsmetode, hvis du nogensinde tilføjer en ny underklasse eller foretager ændringer i, hvilken underklasse, der skal bruges, baseret på hvilket input.

Heldigvis er der en meget mere elegant, men desværre også meget mindre kendt måde at gøre dette på, nemlig ved brug af SysExtension-rammeværket.

Denne ramme udnytter attributter, som du kan bruge til at dekorere dine underklasser for at gøre systemet i stand til at finde ud af, hvilken underklasse der skal bruges til at håndtere hvad. Du skal stadig bruge en konstruktionsmetode, men hvis det gøres korrekt, behøver du aldrig at ændre den, når du tilføjer nye underklasser.

Lad os se på et imaginært eksempel og sige, at du vil implementere et hierarki, der udfører en form for behandling baseret på InventTrans-tabellen. Hvilken behandling, der skal foretages, afhænger af journalernes StatusReceipt og StatusIssue, samt om journalerne er relateret til SalesLine, PurchLine eller ingen af ​​dem. Allerede nu ser du på mange forskellige kombinationer.

Lad os så sige, at du ved, at du indtil videre kun behøver at håndtere en håndfuld af kombinationerne, men du ved også, at du vil blive bedt om at kunne håndtere flere og flere kombinationer med tiden.

Lad os holde det relativt enkelt og sige, at du indtil videre kun behøver at håndtere poster relateret til SalesLine med et StatusIssue af ReservPhysical eller ReservOrdered, alle andre kombinationer kan ignoreres for nu, men da du ved, at du bliver nødt til at håndtere dem senere, vil du gerne designe din kode på en måde, der gør den let at udvide.

Dit hierarki kan se nogenlunde sådan ud for nu:

  • MyProcessor
    • MyProcessor_Salg
      • MyProcessor_Sales_ReservOrdered
      • MyProcessor_Sales_ReservPhysical

Nu kan du nemt implementere en metode i superklassen, der instansierer en underklasse baseret på en ModuleInventPurchSales og en StatusIssue enum. Men du bliver så nødt til at ændre superklassen, hver gang du tilføjer en underklasse, og det er egentlig ikke ideen med nedarvning i objektorienteret programmering. Du behøver trods alt ikke at ændre RunBaseBatch eller SysOperationServiceBase, hver gang du tilføjer et nyt batchjob.

I stedet kan du gøre brug af SysExtension-rammen. Det vil kræve, at du tilføjer en anden klasse, som skal udvide SysAttribute. Denne klasse vil blive brugt som den egenskab, du kan dekorere dine behandlingsklasser med.

Denne klasse ligner meget, hvordan du ville lave en datakontraktklasse til en SysOperation-implementering, idet den vil have nogle datamedlemmer og parm-metoder til at hente og indstille disse værdier.

I vores tilfælde kan ClassDeclaration se nogenlunde sådan ud:

class MyProcessorSystemAttribute extends SysAttribute
{
    ModuleInventPurchSales  module;
    StatusIssue             statusIssue;
    StatusReceipt           statusReceipt
}

Du skal lave en new() metode til at instansiere alle datamedlemmer. Hvis du ønsker, kan du give nogle eller alle af dem standardværdier, men det har jeg ikke gjort.

public void new(ModuleInventPurchSales  _module,
                StatusIssue             _statusIssue,
                StatusReceipt           _statusReceipt)
{
    ;

    super();

    module          = _module;
    statusIssue     = _statusIssue;
    statusReceipt   = _statusReceipt;
}

Og du bør også implementere en parm-metode for hvert datamedlem, men dem har jeg udeladt her, da jeg er sikker på, du ved, hvordan du gør det - ellers, lad os betragte det som en øvelse ;-)

Nu kan du bruge din egenskabsklasse til at dekorere hver af dine behandlingsklasser. For eksempel kunne klasseerklæringerne se sådan ud:

[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
                            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 selvfølgelig navngive dine klasser, som du vil, den vigtige del her er, at du dekorerer dine klasser med egenskaber, der svarer til, hvilken form for behandling de laver. (Men husk på, at der er navnekonventioner for klassehierarkier i Dynamics AX, og det er altid en god idé at følge dem, hvis det er muligt).

Nu hvor du har dekoreret dine klasser for at identificere, hvilken form for behandling hver af dem gør, kan du drage fordel af SysExtension-rammen til at instantiere objekter af underklasserne efter behov.

I din superklasse (MyProcessor) kan du tilføje en konstruktionsmetode som denne:

public static MyProcessor construct(ModuleInventPurchSales _module,
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 virkelig interessante del - og egentlig objektet (undskyld ordspillet) i hele dette indlæg - er metoden getClassFromSysAttribute() i klassen SysExtensionAppClassFactory. Hvad denne metode gør, er, at den accepterer navnet på superklassen i et hierarki (og denne superklasse behøver ikke at være øverst i hierarkiet; det betyder simpelthen, at kun klasser, der udvider denne klasse, vil være kvalificerede) og et attributobjekt.

Det returnerer derefter et objekt af en klasse, der udvider den angivne superklasse og er dekoreret med en tilsvarende attribut.

Du kan naturligvis tilføje så meget yderligere validering eller logik til konstruktionsmetoden, som du ønsker, men den vigtige takeaway her er, at når den først er implementeret, skal du aldrig skulle ændre denne metode igen. Du kan tilføje underklasser til hierarkiet, og så længe du sørger for at dekorere dem korrekt, vil konstruktionsmetoden finde dem, selvom de ikke eksisterede, da den blev skrevet.

Hvad med ydeevne? Jeg har ærligt talt ikke forsøgt at benchmarke det, men min mavefornemmelse er, at det nok klarer sig dårligere end det klassiske switch statement-design. Men i betragtning af, at langt de fleste ydeevneproblemer i Dynamics AX er forårsaget af databaseadgang, ville jeg ikke bekymre mig for meget om det.

Selvfølgelig, hvis du implementerer noget, der vil kræve, at tusindvis af objekter oprettes hurtigt, vil du måske undersøge nærmere, men i de klassiske tilfælde, hvor du bare instansierer et enkelt objekt for at udføre en langvarig behandling, tvivler jeg på, at det vil betyde noget. I betragtning af mit tip til fejlfinding (næste afsnit), ser det ud til, at SysExtension-rammen er afhængig af caching, så i et kørende system tvivler jeg på, at det har et væsentligt præstationshit. Fejlfinding: Hvis konstruktionsmetoden ikke finder dine underklasser, selvom du er sikker på, at de er dekoreret korrekt, kan det være et caching-problem. Prøv at rydde caches på både klient og server. Det burde ikke være nødvendigt at genstarte AOS, men det kan være sidste udvej.

Del på BlueskyDel på FacebookDel på LinkedInDel på TumblrDel på XDel på LinkedInFastgør på Pinterest

Mikkel Bang Christensen

Om forfatteren

Mikkel Bang Christensen
Mikkel er skaberen og ejeren af miklix.com. Han har over 20 års erfaring som professionel computerprogrammør/softwareudvikler og er i øjeblikket fuldtidsansat i en stor europæisk IT-virksomhed. Når han ikke blogger, bruger han sin fritid på en lang række interesser, hobbyer og aktiviteter, som i et vist omfang afspejles i de mange forskellige emner, der dækkes på dette websted.