Utilisation du cadre SysExtension pour savoir quelle sous-classe instancier dans Dynamics AX 2012
Publié : 16 février 2025 à 00 h 30 min 16 s UTC
Cet article décrit comment utiliser la structure peu connue SysExtension dans Dynamics AX 2012 et Dynamics 365 for Operations pour instancier des sous-classes en fonction des décorations d’attributs, permettant une conception facilement extensible d’une hiérarchie de classes de traitement.
Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012
Les informations contenues dans cet article sont basées sur Dynamics AX 2012 R3. Il peut ou non être valide pour d’autres versions. (Mise à jour : je peux confirmer que les informations contenues dans cet article sont également valides pour Dynamics 365 for Operations)
Lors de l’implémentation de classes de traitement dans Dynamics AX, vous êtes souvent confronté à la création d’une hiérarchie de classes dans laquelle chaque sous-classe correspond à une valeur enum ou a un autre couplage de données. Une conception classique consiste à avoir ensuite une méthode de construction dans la super classe, qui a un commutateur qui détermine quelle classe instancier en fonction de l’entrée.
Cela fonctionne bien en principe, mais si vous avez de nombreuses entrées possibles différentes (de nombreux éléments dans un enum ou peut-être que l’entrée est une combinaison de plusieurs valeurs différentes), cela peut devenir fastidieux et sujet aux erreurs à maintenir et la conception a toujours l’inconvénient que vous devrez modifier ladite méthode de construction si jamais vous ajoutez une nouvelle sous-classe ou apportez des modifications à quelle sous-classe doit être utilisée en fonction de quelle entrée.
Heureusement, il existe une façon beaucoup plus élégante, mais malheureusement aussi beaucoup moins connue, de le faire, à savoir par l’utilisation du cadre SysExtension.
Ce cadre tire parti des attributs que vous pouvez utiliser pour décorer vos sous-classes afin de rendre le système capable de déterminer quelle sous-classe doit être utilisée pour gérer quoi. Vous aurez toujours besoin d’une méthode de construction, mais si elle est effectuée correctement, vous n’aurez jamais à la modifier lors de l’ajout de nouvelles sous-classes.
Regardons un exemple imaginaire et disons que vous allez implémenter une hiérarchie qui effectue une sorte de traitement basé sur la table InventTrans. Le traitement à effectuer dépend de StatusReceipt et StatusIssue des enregistrements, ainsi que du fait que les enregistrements soient liés à SalesLine, PurchLine ou ni l’un ni l’autre. Déjà maintenant, vous regardez beaucoup de combinaisons différentes.
Disons alors que vous savez que pour l’instant, vous n’avez besoin de gérer qu’une poignée de combinaisons, mais vous savez aussi qu’il vous sera demandé de pouvoir gérer de plus en plus de combinaisons au fil du temps.
Gardons les choses relativement simples et disons que pour l’instant, il vous suffit de gérer les enregistrements liés à SalesLine avec un StatusIssue de ReservPhysical ou ReservOrdered, toutes les autres combinaisons peuvent être ignorées pour l’instant, mais puisque vous savez que vous devrez les gérer plus tard, vous voudrez concevoir votre code d’une manière qui le rend facilement extensible.
Votre hiérarchie peut ressembler à ceci pour l’instant :
- MyProcessor
- MyProcessor_Sales
- MyProcessor_Sales_ReservOrdered
- MyProcessor_Sales_ReservPhysical
- MyProcessor_Sales
Maintenant, vous pouvez facilement implémenter une méthode dans la super classe qui instancie une sous-classe basée sur un ModuleInventPurchSales et un StatusIssue enum. Mais vous devrez alors modifier la super classe chaque fois que vous ajoutez une sous-classe, et ce n’est pas vraiment l’idée de l’héritage dans la programmation orientée objet. Après tout, vous n’avez pas besoin de modifier RunBaseBatch ou SysOperationServiceBase chaque fois que vous ajoutez un nouveau traitement par lots.
Au lieu de cela, vous pouvez utiliser le framework SysExtension. Cela vous obligera à ajouter une autre classe, qui doit étendre SysAttribute. Cette classe sera utilisée comme attribut avec lequel vous pouvez décorer vos classes de traitement.
Cette classe est très similaire à la façon dont vous créeriez une classe de contrat de données pour une implémentation SysOperation, en ce sens qu’elle aura des membres de données et des méthodes de parm pour obtenir et définir ces valeurs.
Dans notre cas, la Déclaration de classe peut ressembler à ceci :
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Vous devez créer une nouvelle méthode() pour instancier tous les membres de données. Si vous le souhaitez, vous pouvez donner à certains ou à tous les valeurs par défaut, mais je ne l’ai pas fait.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Et vous devriez également implémenter une méthode parm pour chaque membre de données, mais j’ai omis ceux ici car je suis sûr que vous savez comment le faire - sinon, considérons-le comme un exercice ;-)
Vous pouvez maintenant utiliser votre classe d’attributs pour décorer chacune de vos classes de traitement. Par exemple, les déclarations de classe peuvent ressembler à ceci :
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
{
}
Vous pouvez bien sûr nommer vos classes comme vous le souhaitez, la partie importante ici est que vous décorez vos classes avec des attributs qui correspondent au type de traitement qu’elles font. (Mais gardez à l’esprit qu’il existe des conventions de nommage pour les hiérarchies de classe dans Dynamics AX et c’est toujours une bonne idée de les suivre, si possible).
Maintenant que vous avez décoré vos cours pour identifier le type de traitement de chacun d’eux, vous pouvez tirer parti de la structure SysExtension pour instancier les objets des sous-classes selon les besoins.
Dans votre super classe (MyProcessor), vous pouvez ajouter une méthode de construction comme celle-ci :
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;
}
La partie vraiment intéressante - et vraiment l’objet (pardonnez le jeu de mots) de tout ce post - est la méthode getClassFromSysAttribute() dans la classe SysExtensionAppClassFactory. Ce que cette méthode fait, c’est qu’elle accepte le nom de la super classe d’une hiérarchie (et cette super classe n’a pas besoin d’être en haut de la hiérarchie ; cela signifie simplement que seules les classes étendant cette classe seront éligibles) et un objet attributaire.
Il renvoie ensuite un objet d’une classe qui étend la super classe spécifiée et est décoré d’un attribut correspondant.
Vous pouvez évidemment ajouter autant de validation ou de logique supplémentaire à la méthode de construction que vous le souhaitez, mais ce qu’il faut retenir ici, c’est qu’une fois implémentée, vous ne devriez plus jamais avoir à modifier cette méthode. Vous pouvez ajouter des sous-classes à la hiérarchie et tant que vous vous assurez de les décorer de manière appropriée, la méthode de construction les trouvera même si elles n’existaient pas au moment de son écriture.
Qu’en est-il de la performance ? Honnêtement, je n’ai pas essayé de le comparer, mais mon intuition est que cela fonctionne probablement moins bien que la conception classique de l’instruction switch. Cependant, étant donné que de loin les problèmes de performances les plus importants dans Dynamics AX sont causés par l’accès à la base de données, je ne m’inquiéterais pas trop à ce sujet.
Bien sûr, si vous implémentez quelque chose qui nécessitera des milliers d’objets à créer rapidement, vous voudrez peut-être enquêter davantage, mais dans les cas classiques où vous instanciez simplement un seul objet pour effectuer un long traitement, je doute que cela ait de l’importance. En outre, compte tenu de mon conseil de dépannage (paragraphe suivant), il semble que le framework SysExtension repose sur la mise en cache, donc dans un système en cours d’exécution, je doute qu’il ait un impact significatif sur les performances. Dépannage : si la méthode de construction ne trouve pas vos sous-classes même si vous êtes certain qu’elles sont décorées correctement, il peut s’agir d’un problème de mise en cache. Essayez d’effacer les caches sur le client et le serveur. Il ne devrait pas être nécessaire de redémarrer l’AOS, mais il peut s’agir d’un dernier recours.