Mithilfe des SysExtension-Frameworks ermitteln, welche Unterklasse in Dynamics AX 2012 instanziiert werden soll
Veröffentlicht: 16. Februar 2025 um 00:25:37 UTC
Zuletzt aktualisiert: 12. Januar 2026 um 08:42:55 UTC
Dieser Artikel beschreibt, wie man das wenig bekannte SysExtension-Framework in Dynamics AX 2012 und Dynamics 365 for Operations verwendet, um Unterklassen basierend auf Attributdekorationen zu instanziieren und so eine einfach erweiterbare Struktur einer Verarbeitungsklassenhierarchie zu ermöglichen.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
Die Informationen in diesem Beitrag basieren auf Dynamics AX 2012 R3. Sie gelten möglicherweise nicht für andere Versionen. (Update: Ich kann bestätigen, dass die Informationen in diesem Artikel auch für Dynamics 365 for Operations gelten.)
Bei der Implementierung von Verarbeitungsklassen in Dynamics AX steht man häufig vor der Herausforderung, eine Klassenhierarchie zu erstellen, in der jede Unterklasse einem Enum-Wert entspricht oder eine andere Datenkopplung aufweist. Ein klassisches Design sieht vor, in der Oberklasse eine Konstruktormethode zu definieren, die einen Schalter enthält, der anhand der Eingabe die zu instanziierende Klasse bestimmt.
Prinzipiell funktioniert das gut, aber wenn es viele verschiedene mögliche Eingaben gibt (viele Elemente in einer Aufzählung oder die Eingabe ist eine Kombination aus mehreren verschiedenen Werten), kann die Wartung mühsam und fehleranfällig werden. Außerdem hat dieser Entwurf immer den Nachteil, dass die Konstruktormethode angepasst werden muss, wenn eine neue Unterklasse hinzugefügt oder Änderungen daran vorgenommen werden, welche Unterklasse basierend auf welcher Eingabe verwendet werden soll.
Glücklicherweise gibt es eine wesentlich elegantere, aber leider auch viel weniger bekannte Methode, dies zu tun, nämlich durch die Verwendung des SysExtension-Frameworks.
Dieses Framework nutzt Attribute, mit denen Sie Ihre Unterklassen auszeichnen können, damit das System erkennt, welche Unterklasse für welche Aufgaben verwendet werden soll. Sie benötigen weiterhin eine Konstruktormethode, aber bei korrekter Implementierung müssen Sie diese beim Hinzufügen neuer Unterklassen nicht mehr anpassen.
Betrachten wir ein fiktives Beispiel: Angenommen, Sie implementieren eine Hierarchie, die eine Verarbeitung basierend auf der Tabelle „InventTrans“ durchführt. Welche Verarbeitung erfolgt, hängt vom Status „Belegstatus“ und „Ausgabestatus“ der Datensätze ab, sowie davon, ob die Datensätze mit „Verkaufszeile“, „Einkaufszeile“ oder keiner von beiden verknüpft sind. Schon jetzt ergeben sich zahlreiche Kombinationsmöglichkeiten.
Nehmen wir also an, Sie wissen, dass Sie vorerst nur eine Handvoll Kombinationen bewältigen müssen, wissen aber auch, dass Sie im Laufe der Zeit immer mehr Kombinationen bewältigen müssen.
Um es relativ einfach zu halten, gehen wir davon aus, dass Sie vorerst nur Datensätze verarbeiten müssen, die sich auf SalesLine mit dem StatusIssue ReservPhysical oder ReservOrdered beziehen. Alle anderen Kombinationen können vorerst ignoriert werden. Da Sie aber wissen, dass Sie diese später verarbeiten müssen, sollten Sie Ihren Code so gestalten, dass er leicht erweiterbar ist.
Ihre Hierarchie könnte vorerst etwa so aussehen:
- MyProcessorMyProcessor_SalesMyProcessor_Sales_ReservOrderedMyProcessor_Sales_ReservPhysical
Man könnte zwar problemlos eine Methode in der Oberklasse implementieren, die eine Unterklasse basierend auf einem ModuleInventPurchSales- und einem StatusIssue-Enum instanziiert. Allerdings müsste man dann die Oberklasse jedes Mal anpassen, wenn man eine Unterklasse hinzufügt, was nicht dem eigentlichen Sinn der Vererbung in der objektorientierten Programmierung entspricht. Schließlich muss man ja auch nicht RunBaseBatch oder SysOperationServiceBase jedes Mal ändern, wenn man einen neuen Batch-Job hinzufügt.
Alternativ können Sie das SysExtension-Framework verwenden. Dazu müssen Sie eine weitere Klasse hinzufügen, die von SysAttribute erbt. Diese Klasse dient als Attribut, mit dem Sie Ihre Verarbeitungsklassen ausstatten können.
Diese Klasse ist sehr ähnlich wie eine Datenvertragsklasse für eine SysOperation-Implementierung, da sie einige Datenmember und Parametermethoden zum Abrufen und Festlegen dieser Werte enthält.
In unserem Fall könnte die Klassendeklaration etwa so aussehen:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Sie müssen eine `new()`-Methode erstellen, um alle Datenmember zu instanziieren. Optional können Sie einigen oder allen Standardwerte zuweisen, ich habe dies jedoch nicht getan.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Und Sie sollten auch für jedes Datenelement eine `parm`-Methode implementieren, aber ich habe diese hier weggelassen, da ich sicher bin, dass Sie wissen, wie das geht – ansonsten betrachten wir es einfach als Übung ;-)
Nun können Sie Ihre Attributklasse verwenden, um jede Ihrer Verarbeitungsklassen zu dekorieren. Die Klassendeklarationen könnten beispielsweise so aussehen:
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
{
}
Sie können Ihre Klassen natürlich beliebig benennen. Wichtig ist nur, dass Sie Ihre Klassen mit Attributen versehen, die ihrer jeweiligen Verarbeitungsfunktion entsprechen. (Beachten Sie jedoch, dass es in Dynamics AX Namenskonventionen für Klassenhierarchien gibt. Es empfiehlt sich, diese nach Möglichkeit einzuhalten.)
Nachdem Sie Ihre Klassen nun so gestaltet haben, dass sie die Art der Verarbeitung kennzeichnen, die jede von ihnen durchführt, können Sie das SysExtension-Framework nutzen, um bei Bedarf Objekte der Unterklassen zu instanziieren.
In Ihrer Oberklasse (MyProcessor) könnten Sie eine Konstruktormethode wie diese hinzufügen:
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;
}
Das wirklich Interessante – und eigentlich der Kern dieses Beitrags – ist die Methode `getClassFromSysAttribute()` in der Klasse `SysExtensionAppClassFactory`. Diese Methode akzeptiert den Namen der Oberklasse einer Hierarchie (diese Oberklasse muss nicht an oberster Stelle der Hierarchie stehen; es bedeutet lediglich, dass nur Klassen, die von dieser Klasse erben, infrage kommen) und ein Attributobjekt.
Anschließend wird ein Objekt einer Klasse zurückgegeben, die die angegebene Oberklasse erweitert und mit einem entsprechenden Attribut versehen ist.
Sie können die Konstruktormethode natürlich beliebig um weitere Validierungen oder Logik ergänzen. Wichtig ist jedoch, dass diese Methode nach ihrer Implementierung nicht mehr geändert werden muss. Sie können der Hierarchie Unterklassen hinzufügen, und solange Sie diese entsprechend kennzeichnen, findet die Konstruktormethode sie auch dann, wenn sie bei ihrer Erstellung noch nicht existierten.
Wie sieht es mit der Performance aus? Ehrlich gesagt habe ich keine Benchmarks durchgeführt, aber ich vermute, dass diese Variante schlechter abschneidet als die klassische Switch-Anweisung. Da die meisten Performance-Probleme in Dynamics AX jedoch auf Datenbankzugriffe zurückzuführen sind, würde ich mir darüber keine allzu großen Sorgen machen.
Wenn Sie etwas implementieren, das die schnelle Erstellung Tausender Objekte erfordert, sollten Sie dies natürlich genauer untersuchen. In den klassischen Fällen, in denen Sie lediglich ein einzelnes Objekt für eine aufwendige Verarbeitung instanziieren, dürfte es jedoch kaum eine Rolle spielen. Da das SysExtension-Framework offenbar auf Caching basiert, bezweifle ich, dass es im laufenden Betrieb zu signifikanten Leistungseinbußen führt. (Siehe meinen Tipp zur Fehlerbehebung im nächsten Absatz.)
Fehlerbehebung: Falls die Konstruktormethode Ihre Unterklassen nicht findet, obwohl Sie sicher sind, dass diese korrekt dekoriert sind, könnte ein Cache-Problem vorliegen. Leeren Sie die Caches auf Client- und Serverseite. Ein Neustart des AOS ist in der Regel nicht erforderlich, kann aber als letzte Möglichkeit in Betracht gezogen werden.
Weitere Informationen
Wenn Ihnen dieser Beitrag gefallen hat, könnten Ihnen auch diese Vorschläge gefallen:
- Stringformatierung mit Makro und strFmt in Dynamics AX 2012
- So iterieren Sie über die Elemente einer Enumeration aus X++-Code in Dynamics AX 2012
- Löschen einer juristischen Person (Unternehmenskonten) in Dynamics AX 2012
