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
Poslední aktualizace: 12. ledna 2026 v 8:42:52 UTC

Tento článek popisuje, jak v aplikacích Dynamics AX 2012 a Dynamics 365 for Operations použít málo známý framework SysExtension 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 aplikaci Dynamics AX 2012 R3. Pro jiné verze mohou, ale nemusí být platné. (Aktualizace: Mohu potvrdit, že informace v tomto článku platí i pro Dynamics 365 for Operations.)

Při implementaci tříd zpracování v Dynamics AX se často setkáváte s vytvářením hierarchie tříd, ve které každá podtřída odpovídá výčtové hodnotě nebo má nějaké jiné datové propojení. Klasický návrh spočívá v tom, že se v nadřazené třídě nachází metoda konstrukce s přepínačem, který určuje, která třída se má na základě vstupu vytvořit.

V principu to funguje dobře, ale pokud máte mnoho různých možných vstupů (mnoho prvků ve výčtu nebo je vstup kombinací několika různých hodnot), může se jeho údržba stát zdlouhavou a náchylnou k chybám a návrh má vždy tu nevýhodu, že budete muset upravit danou metodu konstrukce, pokud někdy přidáte novou podtřídu nebo provedete změny v tom, která podtřída by se měla použít 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 framework využívá atributy, které můžete použít k dekoraci vašich podtříd, aby systém dokázal zjistit, která podtřída by měla být použita pro co. Stále budete potřebovat metodu construct, ale pokud je provedena správně, nebudete ji muset při přidávání nových podtříd nikdy upravovat.

Podívejme se na imaginární příklad. Řekněme, že implementujete hierarchii, která provádí nějaký druh zpracování na základě tabulky InventTrans. Výběr zpracování závisí na záznamech StatusReceipt a StatusIssue a také na tom, zda se záznamy vztahují k SalesLine, PurchLine nebo k žádnému z nich. Již nyní se díváte na mnoho různých kombinací.

Řekněme tedy, že víte, že prozatím potřebujete zvládnout jen hrstku kombinací, ale také víte, že budete muset časem zvládnout stále více kombinací.

Zjednodušme si to a řekněme, že prozatím potřebujete zpracovávat pouze záznamy související s SalesLine s hodnotou StatusIssue typu ReservPhysical nebo ReservOrdered. Všechny ostatní kombinace lze prozatím ignorovat, ale protože víte, že se s nimi budete muset zabývat později, budete chtít navrhnout kód tak, aby byl snadno rozšiřitelný.

Vaše hierarchie by prozatím mohla vypadat nějak takto:

  • MůjProcesorMůjProcesorProdejMůjProcesor_Prodejní_RezervaObjednanéMůjProcesor_Prodejní_RezervaFyzické

Nyní byste mohli snadno implementovat metodu v nadřazené třídě, která vytvoří instanci podtřídy na základě ModuleInventPurchSales a výčtu StatusIssue. Pak byste ale museli nadřazenou třídu upravovat pokaždé, když přidáte podtřídu, a to není ve skutečnosti 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 použít framework SysExtension. To bude vyžadovat přidání další třídy, která musí rozšiřovat 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ého kontraktu pro implementaci SysOperation, v tom, že bude mít některé datové členy a metody parm pro získávání a nastavování těchto hodnot.

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

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

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

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

    super();

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

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

Nyní můžete použít třídu atributů k dekoraci každé z vašich tříd zpracování. Deklarace tříd by mohly 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 jakkoli chcete, důležité je však přiřadit atributy, které odpovídají typu zpracování, které provádějí. (Mějte však na paměti, že v Dynamics AX existují konvence pojmenování pro hierarchie tříd a je vždy dobré se jimi řídit, pokud je to možné).

Nyní, když jste si upravili třídy tak, aby identifikovaly, jaký druh zpracování každá z nich provádí, můžete využít framework SysExtension k vytvoření instancí objektů podtříd podle potřeby.

Ve vaší nadtřídě (MyProcessor) můžete přidat metodu konstruktu takto:

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ímavá část – a vlastně i cíl (omlouvám se za slovní hříčku) celého tohoto příspěvku – je metoda getClassFromSysAttribute() ve třídě SysExtensionAppClassFactory. Tato metoda přijímá název nadřazené třídy hierarchie (a tato nadřazená třída nemusí být na vrcholu hierarchie; znamená to jednoduše, že způsobilé budou pouze třídy rozšiřující tuto třídu) a objekt atributu.

Poté vrací objekt třídy, která rozšiřuje zadanou nadtřídu a je doplněna odpovídajícím atributem.

Do metody construct můžete samozřejmě přidat tolik dalších validací nebo logiky, kolik chcete, ale důležitým poznatkem je, že jakmile ji jednou implementujete, už byste ji nikdy neměli muset upravovat. Do hierarchie můžete přidat podtřídy a pokud je vhodně ozdobíte, metoda construct je najde, i když v době jejího napsání neexistovaly.

Co výkon? Upřímně jsem se to nesnažil porovnávat, ale mám pocit, že to pravděpodobně funguje hůř než klasický návrh s příkazem switch. 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 moc nedělal starosti.

Samozřejmě, pokud implementujete něco, co bude vyžadovat rychlé vytvoření tisíců objektů, možná budete chtít podrobněji prozkoumat tuto problematiku, ale v klasických případech, kdy vytvoříte pouze instanci jednoho objektu pro zdlouhavé zpracování, pochybuji, že to bude mít význam. Také s ohledem na můj tip na řešení problémů (další odstavec) se zdá, že framework SysExtension se spoléhá na ukládání do mezipaměti, takže v běžícím systému pochybuji, že má výrazný dopad na výkon.

Řešení problémů: Pokud metoda construct nenajde vaše podtřídy, i když jste si jisti, že jsou správně dekorovány, může se jednat o problém s mezipamětí. Zkuste vymazat mezipaměť na klientovi i serveru. Restart AOS by neměl být nutný, ale může to být až poslední možnost.

Další čtení

Pokud se vám tento příspěvek líbil, mohly by se vám líbit i tyto návrhy:


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

Mikkel Christensen

O autorovi

Mikkel 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.