Miklix

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.


Cette page a été traduite de l'anglais afin de la rendre accessible au plus grand nombre. Malheureusement, la traduction automatique n'est pas encore une technologie parfaite, et des erreurs peuvent donc se produire. Si vous préférez, vous pouvez consulter la version originale en anglais ici :

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 :

class MyProcessorSystemAttribute extends SysAttribute
{
    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.

public void new(ModuleInventPurchSales  _module,
                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 :

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

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 :

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

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 :


Partager sur BlueskyPartager sur FacebookPartager sur LinkedInPartager sur TumblrPartager sur XPartager sur LinkedInÉpingler sur Pinterest

Mikkel Christensen

A propos de l'auteur

Mikkel Christensen
Mikkel est le créateur et le propriétaire de miklix.com. Il a plus de 20 ans d'expérience en tant que programmeur informatique professionnel/développeur de logiciels et travaille actuellement à plein temps pour une grande entreprise européenne de TI. Lorsqu'il ne blogue pas, il consacre son temps libre à un large éventail d'intérêts, de passe-temps et d'activités, ce qui peut se refléter dans une certaine mesure dans la variété des sujets abordés sur ce site web.