Utilisation du framework SysExtension pour déterminer quelle sous-classe instancier dans Dynamics AX 2012
Publié : 16 février 2025 à 00:25:41 UTC
Dernière mise à jour : 12 janvier 2026 à 08:42:59 UTC
Cet article décrit comment utiliser le framework SysExtension, peu connu, dans Dynamics AX 2012 et Dynamics 365 for Operations pour instancier des sous-classes basées sur 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. Leur validité pour d'autres versions n'est pas garantie. (Mise à jour : Je confirme que ces informations sont également valables pour Dynamics 365 for Operations.)
Lors de l'implémentation de classes de traitement dans Dynamics AX, il est souvent nécessaire de créer une hiérarchie de classes où chaque sous-classe correspond à une valeur d'énumération ou présente un autre type de couplage de données. Une conception classique consiste alors à définir une méthode `construct` dans la super-classe, munie d'un paramètre `switch` qui détermine la classe à instancier en fonction de l'entrée.
En principe, cela fonctionne bien, mais si vous avez de nombreuses entrées possibles (de nombreux éléments dans une énumération ou une combinaison de plusieurs valeurs différentes), la maintenance peut devenir fastidieuse et sujette aux erreurs. De plus, cette conception présente toujours l'inconvénient de nécessiter la modification de la méthode de construction si vous ajoutez une nouvelle sous-classe ou si vous modifiez la sous-classe à utiliser en fonction de l'entrée.
Heureusement, il existe une manière beaucoup plus élégante, mais malheureusement aussi beaucoup moins connue, de procéder, à savoir l'utilisation du framework SysExtension.
Ce framework exploite des attributs permettant de décorer vos sous-classes afin que le système puisse déterminer quelle sous-classe est appropriée pour chaque tâche. Une méthode de construction reste nécessaire, mais si elle est correctement implémentée, vous n'aurez plus besoin de la modifier lors de l'ajout de nouvelles sous-classes.
Prenons un exemple fictif : vous souhaitez implémenter une hiérarchie qui effectue un traitement à partir de la table InventTrans. Le traitement à appliquer dépend des statuts StatusReceipt et StatusIssue des enregistrements, ainsi que de leur appartenance à une ligne de vente (SalesLine), une ligne d'achat (PurchLine) ou à aucune des deux. Vous vous retrouvez déjà face à un grand nombre de combinaisons possibles.
Supposons donc que vous sachiez que pour l'instant vous n'avez besoin de gérer qu'une poignée de combinaisons, mais que vous sachiez également qu'on vous demandera de pouvoir en gérer de plus en plus au fil du temps.
Restons relativement simples et disons que pour l'instant, vous n'avez besoin de traiter que les enregistrements liés à SalesLine avec un StatusIssue de ReservPhysical ou ReservOrdered ; toutes les autres combinaisons peuvent être ignorées pour le moment, mais comme vous savez que vous devrez les traiter plus tard, vous voudrez concevoir votre code de manière à le rendre facilement extensible.
Votre hiérarchie pourrait ressembler à ceci pour le moment :
- MonProcesseurMonProcesseur_VentesMonProcesseur_Ventes_RéserveCommandéMonProcesseur_Ventes_RéservePhysique
Vous pourriez facilement implémenter une méthode dans la superclasse qui instancie une sous-classe à partir d'un module `ModuleInventPurchSales` et d'une énumération `StatusIssue`. Cependant, vous devriez alors modifier la superclasse à chaque ajout d'une sous-classe, ce qui va à l'encontre du principe d'héritage en programmation orientée objet. Après tout, vous n'avez pas besoin de modifier `RunBaseBatch` ou `SysOperationServiceBase` à chaque ajout d'un nouveau traitement par lots.
Vous pouvez utiliser le framework SysExtension. Cela nécessite l'ajout d'une classe supplémentaire, qui doit étendre SysAttribute. Cette classe servira d'attribut pour 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 comportera des membres de données et des méthodes parm pour obtenir et définir ces valeurs.
Dans notre cas, la déclaration de classe pourrait ressembler à ceci :
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Il vous faut créer une méthode `new()` pour instancier tous les membres de données. Vous pouvez, si vous le souhaitez, leur attribuer des valeurs par défaut, mais je ne l'ai pas fait.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
Vous devriez également implémenter une méthode parm pour chaque membre de données, mais je les ai omises ici car je suis sûr que vous savez comment faire ; sinon, considérons cela comme un exercice ;-)
Vous pouvez désormais utiliser votre classe d'attributs pour décorer chacune de vos classes de traitement. Par exemple, les déclarations de classe pourraient 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 ; l’important est de les doter d’attributs correspondant au type de traitement qu’elles effectuent. (N’oubliez pas cependant qu’il existe des conventions de nommage pour les hiérarchies de classes dans Dynamics AX et qu’il est toujours préférable de les respecter, si possible.)
Maintenant que vous avez décoré vos classes pour identifier le type de traitement que chacune effectue, vous pouvez tirer parti du framework SysExtension pour instancier des objets des sous-classes selon vos besoins.
Dans votre superclasse (MyProcessor), vous pouvez ajouter une méthode de construction comme ceci :
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 le véritable objet (sans jeu de mots) de cet article – est la méthode `getClassFromSysAttribute()` de la classe `SysExtensionAppClassFactory`. Cette méthode prend en paramètre le nom de la superclasse d'une hiérarchie (cette superclasse n'a pas besoin d'être au sommet de la hiérarchie ; cela signifie simplement que seules les classes étendant cette classe seront éligibles) et un objet attribut.
Elle renvoie ensuite un objet d'une classe qui étend la superclasse spécifiée et qui est décorée d'un attribut correspondant.
Vous pouvez bien sûr ajouter autant de validations ou de logique que vous le souhaitez à la méthode `construct`, mais l'important est qu'une fois implémentée, vous ne devriez plus jamais avoir à la modifier. Vous pouvez ajouter des sous-classes à la hiérarchie et, pourvu que vous les décoriez correctement, la méthode `construct` les trouvera même si elles n'existaient pas lors de sa création.
Qu'en est-il des performances ? Honnêtement, je n'ai pas effectué de tests de performance, mais j'ai l'impression que cette solution est probablement moins performante que la méthode classique avec instruction switch. Cependant, étant donné que la grande majorité des problèmes de performances dans Dynamics AX sont dus à l'accès à la base de données, je ne m'en inquiéterais pas outre mesure.
Bien sûr, si vous implémentez une fonctionnalité nécessitant la création rapide de milliers d'objets, il peut être judicieux d'approfondir la question. Cependant, dans les cas classiques où l'on instancie un seul objet pour effectuer un traitement long, cela n'a probablement aucune incidence. De plus, compte tenu de mon conseil de dépannage (paragraphe suivant), il semble que le framework SysExtension repose sur la mise en cache ; par conséquent, sur un système en fonctionnement, son impact sur les performances est probablement négligeable.
Dépannage : Si la méthode de construction ne trouve pas vos sous-classes malgré une déclaration correcte, il peut s'agir d'un problème de cache. Essayez de vider les caches côté client et côté serveur. En principe, il n'est pas nécessaire de redémarrer l'AOS, mais cela peut être envisagé en dernier recours.
Lectures complémentaires
Si vous avez apprécié cet article, vous aimerez peut-être aussi ces suggestions :
- Présentation rapide de Dynamics AX 2012 SysOperation Framework
- Appel des services de documents AIF directement depuis X++ dans Dynamics AX 2012
- Supprimer une entité juridique (comptes d'entreprise) dans Dynamics AX 2012
