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
Ostatnia aktualizacja: 12 stycznia 2026 08:43:08 UTC
W tym artykule opisano, jak używać mało znanej infrastruktury 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.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
Informacje zawarte w tym poście dotyczą systemu Dynamics AX 2012 R3. Mogą, ale nie muszą, być aktualne dla innych wersji. (Aktualizacja: Potwierdzam, że informacje zawarte w tym artykule dotyczą również systemu Dynamics 365 for Operations).
Podczas implementacji klas przetwarzania w Dynamics AX często pojawia się konieczność utworzenia hierarchii klas, w której każda podklasa odpowiada wartości wyliczeniowej lub ma inne powiązanie z danymi. Klasycznym rozwiązaniem jest wówczas utworzenie metody konstruktu w nadklasie, która zawiera przełącznik, który na podstawie danych wejściowych określa, która klasa ma zostać utworzona.
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), utrzymanie tego może stać się żmudne i podatne na błędy, 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 których 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 wykorzystuje atrybuty, których można użyć do dekorowania podklas, aby system mógł określić, która podklasa powinna być użyta do obsługi czego. Nadal będziesz potrzebować metody konstrukcyjnej, ale jeśli zostanie ona poprawnie wykonana, nigdy nie będziesz musiał jej modyfikować podczas dodawania nowych podklas.
Przyjrzyjmy się przykładowi i załóżmy, że zamierzasz zaimplementować hierarchię, która wykonuje pewien rodzaj przetwarzania w oparciu o tabelę InventTrans. To, które przetwarzanie zostanie wykonane, zależy od wartości StatusReceipt i StatusIssue rekordów, a także od tego, czy rekordy są powiązane z SalesLine, PurchLine, czy żadnym z nich. Już teraz masz do czynienia z wieloma różnymi kombinacjami.
Załóżmy, że na razie wiesz, że musisz obsłużyć tylko kilka kombinacji, ale wiesz również, że z czasem będziesz proszony o obsługę coraz większej ich liczby.
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ć. Ponieważ jednak wiesz, że będziesz musiał się nimi zająć później, zaprojektuj kod w taki sposób, aby był łatwo rozszerzalny.
Twoja hierarchia może na razie wyglądać następująco:
- MójProcesorMójProcesor_SprzedażMójProcesor_Rezerwacja_SprzedażyZamówioneMójProcesor_Rezerwacja_SprzedażyFizyczna
Teraz można łatwo zaimplementować metodę w superklasie, która tworzy instancję podklasy na podstawie wyliczenia ModuleInventPurchSales i StatusIssue. Ale wtedy trzeba będzie modyfikować superklasę za każdym razem, gdy dodaje się podklasę, a to nie do końca jest ideą dziedziczenia w programowaniu obiektowym. W końcu nie trzeba modyfikować RunBaseBatch ani SysOperationServiceBase za każdym razem, gdy dodaje się nowe zadanie wsadowe.
Zamiast tego możesz skorzystać z frameworka SysExtension. Wymaga to 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, w jaki tworzy się klasę 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:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Musisz utworzyć metodę new() do tworzenia instancji wszystkich elementów danych. Jeśli chcesz, możesz nadać niektórym lub wszystkim z nich wartości domyślne, ale ja tego nie zrobiłem.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Powinieneś również zaimplementować metodę parm dla każdego elementu 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 do udekorowania każdej z klas przetwarzania. Na przykład deklaracje klas mogą wyglądać tak:
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 warto się do nich stosować, jeśli to możliwe).
Teraz, gdy określiłeś już, jaki rodzaj przetwarzania wykonuje każda z klas, możesz skorzystać z infrastruktury SysExtension, aby w razie potrzeby tworzyć wystąpienia obiektów podklas.
W swojej superklasie (MyProcessor) możesz dodać metodę konstrukcji w następujący sposób:
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ącą częścią – i tak naprawdę obiektem (przepraszam za kalambur) całego wpisu – jest metoda getClassFromSysAttribute() w klasie SysExtensionAppClassFactory. Metoda ta akceptuje nazwę nadklasy hierarchii (i ta nadklasa nie musi znajdować się na szczycie hierarchii; oznacza to po prostu, że kwalifikują się tylko klasy rozszerzające tę klasę) oraz obiekt atrybutu.
Następnie zwraca obiekt klasy rozszerzającej określoną superklasę i jest ozdobiony odpowiednim atrybutem.
Oczywiście możesz dodać do metody konstruktu tyle dodatkowej walidacji lub logiki, ile chcesz, ale najważniejsze jest to, że po zaimplementowaniu nie będziesz musiał jej już modyfikować. Możesz dodawać podklasy do hierarchii i o ile zadbasz o ich odpowiednie udekorowanie, metoda konstruktu je znajdzie, nawet jeśli nie istniały w momencie jej pisania.
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 instrukcji switch. Biorąc jednak pod uwagę, że zdecydowanie najwięcej problemów z wydajnością w Dynamics AX wynika z dostępu 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ę, żeby to miało znaczenie. Ponadto, biorąc pod uwagę moją wskazówkę dotyczącą rozwiązywania problemów (w następnym akapicie), wydaje się, że framework SysExtension opiera się na buforowaniu, więc w działającym systemie wątpię, aby miało to znaczący wpływ na wydajność.
Rozwiązywanie problemów: Jeśli metoda konstruowania nie znajduje podklas, mimo że masz pewność, że są poprawnie udekorowane, może to być problem z buforowaniem. Spróbuj wyczyścić buforowanie zarówno po stronie klienta, jak i serwera. Nie powinno być konieczne ponowne uruchomienie AOS, ale może to być ostateczność.
Dalsza lektura
Jeśli podobał Ci się ten wpis, mogą Cię zainteresować również poniższe sugestie:
- Formatowanie ciągu za pomocą makra i strFmt w Dynamics AX 2012
- Jak iterować po elementach wyliczenia z kodu X++ w systemie Dynamics AX 2012
- Różnica między data() i buf2Buf() w Dynamics AX 2012
