Miklix

Korzystanie z struktury SysExtension w celu znalezienia podklasy, którą należy utworzyć w systemie Dynamics AX 2012

Opublikowano: 16 lutego 2025 00:25:54 UTC

W tym artykule opisano, jak używać mało znanej struktury SysExtension w systemie Dynamics AX 2012 i Dynamics 365 for Operations do tworzenia wystąpień podklas na podstawie dekoracji atrybutów, co pozwala na łatwą rozszerzalność projektu hierarchii klas przetwarzania.


Ta strona została przetłumaczona maszynowo z języka angielskiego, aby była dostępna dla jak największej liczby osób. Niestety, tłumaczenie maszynowe nie jest jeszcze dopracowaną technologią, więc mogą wystąpić błędy. Jeśli wolisz, możesz wyświetlić oryginalną angielską wersję tutaj:

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

Informacje w tym poście są oparte na Dynamics AX 2012 R3. Mogą być lub nie być ważne dla innych wersji. (Aktualizacja: Mogę potwierdzić, że informacje w tym artykule są również ważne dla Dynamics 365 for Operations)

Podczas implementacji klas przetwarzania w Dynamics AX często stajesz przed koniecznością utworzenia hierarchii klas, w której każda podklasa odpowiada wartości wyliczeniowej lub ma inne sprzężenie danych. Klasycznym projektem jest następnie posiadanie metody konstruowania w superklasie, która ma przełącznik, który określa, którą klasę utworzyć na podstawie danych wejściowych.

W teorii działa to dobrze, ale jeśli masz wiele różnych możliwych danych wejściowych (wiele elementów w wyliczeniu lub dane wejściowe są kombinacją kilku różnych wartości), może to stać się żmudne i podatne na błędy w utrzymaniu, a projekt zawsze ma tę wadę, że będziesz musiał zmodyfikować wspomnianą metodę konstrukcji, jeśli kiedykolwiek dodasz nową podklasę lub zmienisz, która podklasa powinna zostać użyta na podstawie danych wejściowych.

Na szczęście istnieje o wiele bardziej elegancki, choć niestety mniej znany sposób dokonania tego, a mianowicie wykorzystanie struktury SysExtension.

Ta struktura korzysta z atrybutów, których możesz użyć do dekoracji swoich podklas, aby system mógł ustalić, która podklasa powinna być używana do obsługi czego. Nadal będziesz potrzebować metody konstrukcyjnej, ale jeśli zrobisz to poprawnie, nigdy nie będziesz musiał jej modyfikować podczas dodawania nowych podklas.

Przyjrzyjmy się wyimaginowanemu przykładowi i powiedzmy, że zamierzasz wdrożyć hierarchię, która wykonuje pewien rodzaj przetwarzania na podstawie tabeli InventTrans. To, które przetwarzanie wykonać, zależy od StatusReceipt i StatusIssue rekordów, a także od tego, czy rekordy są powiązane z SalesLine, PurchLine czy żadnym z nich. Już teraz patrzysz na wiele różnych kombinacji.

Załóżmy, że wiesz, iż na razie musisz obsłużyć tylko kilka kombinacji, ale wiesz również, że z czasem będziesz proszony o obsługę coraz większej liczby kombinacji.

Uprośćmy sprawę i załóżmy, że na razie musisz obsługiwać tylko rekordy powiązane z SalesLine o statusie StatusIssue równym ReservPhysical lub ReservOrdered; wszystkie pozostałe kombinacje możesz na razie zignorować, ale skoro wiesz, że będziesz musiał się nimi zająć później, powinieneś zaprojektować kod w taki sposób, aby był łatwo rozszerzalny.

Twoja hierarchia może na razie wyglądać następująco:

  • MójProcesor
    • MójProcesor_Sprzedaż
      • MyProcessor_Sales_ReservOrdered
      • MyProcessor_Sales_ReservePhysical

Teraz możesz łatwo zaimplementować metodę w superklasie, która tworzy instancję podklasy na podstawie ModuleInventPurchSales i wyliczenia StatusIssue. Ale będziesz musiał modyfikować superklasę za każdym razem, gdy dodasz podklasę, a to nie jest tak naprawdę idea dziedziczenia w programowaniu obiektowym. W końcu nie musisz modyfikować RunBaseBatch lub SysOperationServiceBase za każdym razem, gdy dodajesz nowe zadanie wsadowe.

Zamiast tego możesz użyć struktury SysExtension. To będzie wymagało dodania kolejnej klasy, która musi rozszerzać SysAttribute. Ta klasa będzie używana jako atrybut, którym możesz ozdobić swoje klasy przetwarzania.

Ta klasa jest bardzo podobna do sposobu tworzenia klasy kontraktu danych dla implementacji SysOperation, ponieważ będzie zawierać pewne elementy danych i metody parm służące do pobierania i ustawiania tych wartości.

W naszym przypadku ClassDeclaration może wyglądać mniej więcej tak:

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

Musisz utworzyć metodę new() do tworzenia instancji wszystkich członków danych. Jeśli chcesz, możesz nadać niektórym lub wszystkim z nich wartości domyślne, ale tego nie zrobiłem.

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

    super();

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

Powinieneś także zaimplementować metodę parm dla każdego członka danych, ale pominąłem to tutaj, ponieważ jestem pewien, że wiesz, jak to zrobić — w przeciwnym razie potraktujmy to jako ćwiczenie ;-)

Teraz możesz użyć swojej klasy atrybutów, aby ozdobić każdą z klas przetwarzania. Na przykład deklaracje klas mogą wyglądać tak:

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

Oczywiście możesz nazwać swoje klasy w dowolny sposób, ważne jest jednak, aby ozdobić je atrybutami odpowiadającymi rodzajowi przetwarzania, jakie wykonują. (Należy jednak pamiętać, że w systemie Dynamics AX obowiązują konwencje nazewnictwa hierarchii klas i zawsze dobrym pomysłem jest ich przestrzeganie, jeśli to możliwe).

Teraz, gdy uporządkowałeś swoje klasy w taki sposób, aby określić, jaki rodzaj przetwarzania wykonuje każda z nich, możesz skorzystać z infrastruktury SysExtension, aby tworzyć wystąpienia obiektów podklas, jeśli zajdzie taka potrzeba.

W swojej superklasie (MyProcessor) możesz dodać następującą metodę konstrukcji:

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

Naprawdę interesująca część - i tak naprawdę obiekt (przepraszam za kalambur) całego posta - to metoda getClassFromSysAttribute() w klasie SysExtensionAppClassFactory. Ta metoda akceptuje nazwę superklasy hierarchii (a ta superklasa nie musi znajdować się na szczycie hierarchii; oznacza to po prostu, że tylko klasy rozszerzające tę klasę będą uprawnione) i obiekt atrybutu.

Następnie zwraca obiekt klasy rozszerzającej określoną superklasę i jest ozdobiony odpowiednim atrybutem.

Oczywiście możesz dodać tyle dodatkowej walidacji lub logiki do metody konstruktu, ile chcesz, ale ważne jest, aby po zaimplementowaniu nigdy więcej nie trzeba było modyfikować tej metody. Możesz dodawać podklasy do hierarchii i dopóki upewnisz się, że odpowiednio je udekorujesz, metoda konstruktu je znajdzie, nawet jeśli nie istniały, gdy została napisana.

A co z wydajnością? Szczerze mówiąc, nie próbowałem tego testować, ale mam przeczucie, że prawdopodobnie działa gorzej niż klasyczny projekt switch statement. Jednak biorąc pod uwagę, że zdecydowanie najwięcej problemów z wydajnością w Dynamics AX jest spowodowanych dostępem do bazy danych, nie martwiłbym się tym zbytnio.

Oczywiście, jeśli implementujesz coś, co będzie wymagało szybkiego utworzenia tysięcy obiektów, możesz chcieć zbadać to dokładniej, ale w klasycznych przypadkach, gdy po prostu tworzysz pojedynczy obiekt, aby wykonać długotrwałe przetwarzanie, wątpię, aby miało to znaczenie. Ponadto, biorąc pod uwagę moją wskazówkę dotyczącą rozwiązywania problemów (następny akapit), wydaje się, że struktura SysExtension opiera się na buforowaniu, więc wątpię, aby w działającym systemie miało to znaczący wpływ na wydajność. Rozwiązywanie problemów: Jeśli metoda konstruowania nie znajdzie twoich podklas, mimo że jesteś pewien, że są poprawnie udekorowane, może to być problem z buforowaniem. Spróbuj wyczyścić bufory zarówno po stronie klienta, jak i serwera. Nie powinno być konieczne ponowne uruchomienie AOS, ale może to być ostateczność.

Udostępnij na BlueskyUdostępnij na FacebookuUdostępnij na LinkedInUdostępnij na TumblrUdostępnij na XUdostępnij na LinkedInPrzypnij na Pintereście

Mikkel Bang Christensen

O autorze

Mikkel Bang Christensen
Mikkel jest twórcą i właścicielem miklix.com. Ma ponad 20-letnie doświadczenie jako profesjonalny programista komputerowy / programista oprogramowania i jest obecnie zatrudniony na pełny etat w dużej europejskiej korporacji IT. Kiedy nie bloguje, poświęca swój wolny czas na szeroki wachlarz zainteresowań, hobby i aktywności, co może w pewnym stopniu znaleźć odzwierciedlenie w różnorodności tematów poruszanych na tej stronie.