Miklix

Použití SysExtension Framework ke zjištění, kterou podtřídu vytvořit instanci v Dynamics AX 2012

Vydáno: 16. února 2025 v 0:25:35 UTC

Tento článek popisuje, jak používat málo známý rámec SysExtension v Dynamics AX 2012 a Dynamics 365 for Operations k vytváření instancí podtříd na základě dekorací atributů, což umožňuje snadno rozšiřitelný návrh hierarchie tříd zpracování.


Tato stránka byla strojově přeložena z angličtiny, aby byla přístupná co největšímu počtu lidí. Strojový překlad bohužel ještě není dokonalá technologie, takže může dojít k chybám. Pokud si přejete, můžete si prohlédnout původní anglickou verzi zde:

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

Informace v tomto příspěvku jsou založeny na Dynamics AX 2012 R3. Může a nemusí platit pro jiné verze. (Aktualizace: Mohu potvrdit, že informace v tomto článku platí také pro Dynamics 365 for Operations)

Při implementaci tříd zpracování v Dynamics AX se často potýkáte s vytvořením hierarchie tříd, ve které každá podtřída odpovídá hodnotě výčtu nebo má nějakou jinou datovou vazbu. Klasickým návrhem je pak mít metodu konstruktu v supertřídě, která má přepínač, který na základě vstupu určuje, která třída se má vytvořit.

V zásadě to funguje dobře, ale pokud máte mnoho různých možných vstupů (mnoho prvků ve výčtu nebo možná vstup je kombinací několika různých hodnot), může být údržba zdlouhavá a náchylná k chybám a návrh má vždy tu nevýhodu, že budete muset upravit zmíněnou metodu konstruktu, pokud někdy přidáte novou podtřídu nebo provedete změny, která podtřída by měla být použita na základě kterého vstupu.

Naštěstí existuje mnohem elegantnější, ale bohužel také mnohem méně známý způsob, jak toho dosáhnout, a to pomocí frameworku SysExtension.

Tento rámec využívá atributy, které můžete použít k ozdobení svých podtříd, aby byl systém schopen zjistit, která podtřída by měla být použita pro zpracování čeho. Stále budete potřebovat metodu konstruktu, ale pokud je provedena správně, nikdy ji nebudete muset upravovat při přidávání nových podtříd.

Podívejme se na imaginární příklad a řekněme, že se chystáte implementovat hierarchii, která provádí nějaké zpracování na základě tabulky InventTrans. Jaké zpracování provést, závisí na StatusReceipt a StatusIssue záznamů a také na tom, zda záznamy souvisí s SalesLine, PurchLine nebo žádnými. Už teď se díváte na spoustu různých kombinací.

Řekněme tedy, že víte, že zatím potřebujete zvládnout jen hrstku kombinací, ale také víte, že se po vás bude chtít, abyste časem zvládli další a další kombinace.

Necháme to relativně jednoduché a řekněme, že zatím stačí zpracovávat záznamy související s SalesLine s StatusIssue ReservPhysical nebo ReservOrdered, všechny ostatní kombinace lze prozatím ignorovat, ale protože víte, že je budete muset zpracovat později, budete chtít navrhnout svůj kód tak, aby byl snadno rozšiřitelný.

Vaše hierarchie může zatím vypadat nějak takto:

  • Můj Procesor
    • MyProcessor_Sales
      • MyProcessor_Sales_ReservOrdered
      • MyProcessor_Sales_ReservPhysical

Nyní můžete snadno implementovat metodu v supertřídě, která vytvoří instanci podtřídy na základě ModuleInventPurchSales a výčtu StatusIssue. Ale potom budete muset modifikovat supertřídu pokaždé, když přidáte podtřídu, a to ve skutečnosti není myšlenka dědičnosti v objektově orientovaném programování. Koneckonců, nemusíte upravovat RunBaseBatch nebo SysOperationServiceBase pokaždé, když přidáte novou dávkovou úlohu.

Místo toho můžete využít rámec SysExtension. To bude vyžadovat přidání další třídy, která potřebuje rozšířit SysAttribute. Tato třída bude použita jako atribut, kterým můžete ozdobit své třídy zpracování.

Tato třída je velmi podobná tomu, jak byste vytvořili třídu datových kontraktů pro implementaci SysOperation v tom, že bude mít některé datové členy a metody parm pro získání a nastavení těchto hodnot.

V našem případě může ClassDeclaration vypadat nějak takto:

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

Musíte vytvořit metodu new() pro vytváření instance všech datových členů. Pokud chcete, můžete některým nebo všem dát výchozí hodnoty, ale neudělal jsem to.

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

    super();

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

A také byste měli implementovat metodu parm pro každý datový člen, ale ty jsem zde vynechal, protože jsem si jistý, že víte, jak to udělat - jinak to berme jako cvičení ;-)

Nyní můžete pomocí své třídy atributů ozdobit každou ze svých tříd zpracování. Deklarace třídy mohou vypadat například takto:

[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
{
}

Své třídy si samozřejmě můžete pojmenovat, jak chcete, důležité je, že své třídy ozdobíte atributy, které odpovídají tomu, jaké zpracování provádějí. (Ale mějte na paměti, že v Dynamics AX existují konvence pojmenování hierarchií tříd a je vždy dobré se jimi řídit, pokud je to možné).

Nyní, když jste vyzdobili své třídy, abyste zjistili, jaký druh zpracování každá z nich provádí, můžete využít výhod rámce SysExtension k vytvoření instance objektů podtříd podle potřeby.

Ve své super třídě (MyProcessor) můžete přidat metodu konstrukce, jako je tato:

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;
}

Opravdu zajímavou částí – a skutečně předmětem (promiňte slovní hříčku) celého tohoto příspěvku – je metoda getClassFromSysAttribute() ve třídě SysExtensionAppClassFactory. Tato metoda dělá to, že přijímá název nadtřídy hierarchie (a tato supertřída nemusí být na vrcholu hierarchie; jednoduše to znamená, že budou vhodné pouze třídy rozšiřující tuto třídu) a objekt atributu.

Potom vrátí objekt třídy, který rozšiřuje zadanou supertřídu a je ozdoben odpovídajícím atributem.

K metodě konstruktu můžete samozřejmě přidat tolik dalších validací nebo logiky, kolik chcete, ale důležité je, že po implementaci byste tuto metodu už nikdy neměli upravovat. Do hierarchie můžete přidat podtřídy, a pokud se ujistíte, že je vhodně ozdobíte, metoda konstrukt je najde, i když v době, kdy byla napsána, neexistovaly.

Co výkon? Upřímně jsem se to nepokoušel porovnávat, ale mám pocit, že to pravděpodobně funguje hůře než klasický návrh příkazu přepínače. Vzhledem k tomu, že zdaleka nejvíce problémů s výkonem v Dynamics AX je způsobeno přístupem k databázi, bych si s tím ale příliš hlavu nelámal.

Samozřejmě, pokud implementujete něco, co bude vyžadovat rychlé vytvoření tisíců objektů, možná budete chtít dále zkoumat, ale v klasických případech, kdy stačí vytvořit instanci jediného objektu, abyste provedli nějaké zdlouhavé zpracování, pochybuji, že to bude záležet. S ohledem na můj tip pro odstraňování problémů (další odstavec) se také zdá, že framework SysExtension spoléhá na ukládání do mezipaměti, takže v běžícím systému pochybuji, že má významný zásah do výkonu. Odstraňování problémů: Pokud metoda konstruktu nenajde vaše podtřídy, i když jste si jisti, že jsou upraveny správně, může to být problém s ukládáním do mezipaměti. Zkuste vymazat mezipaměť na klientovi i serveru. Nemělo by být nutné restartovat AOS, ale může to být poslední možnost.

Sdílet na BlueskySdílejte na FacebookuSdílet na LinkedInSdílet na TumblrSdílet na XSdílet na LinkedInPřipnout na Pinterest

Mikkel Bang Christensen

O autorovi

Mikkel Bang Christensen
Mikkel je tvůrcem a majitelem webu miklix.com. Má více než 20 let zkušeností jako profesionální programátor/vývojář softwaru a v současné době pracuje na plný úvazek pro velkou evropskou IT společnost. Pokud zrovna nepíše blog, věnuje svůj volný čas široké škále zájmů, koníčků a aktivit, což se může do jisté míry odrážet v rozmanitosti témat na tomto webu.