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

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 en fonction 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. Elles peuvent ou non être valables pour d'autres versions. (Mise à jour : je peux confirmer que les informations contenues dans cet article sont également valables 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 d'énumération ou possède un autre couplage de données. Une conception classique consiste alors à avoir une méthode de construction dans la super-classe, qui possède un commutateur qui détermine la 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 une énumération 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 à 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 tire parti des attributs que vous pouvez utiliser pour décorer vos sous-classes afin de permettre au système 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.

Prenons un exemple imaginaire et imaginons que vous allez implémenter une hiérarchie qui effectue un certain type de traitement basé sur la table InventTrans. Le traitement à effectuer dépend du StatusReceipt et du StatusIssue des enregistrements, ainsi que du fait que les enregistrements soient liés à SalesLine, PurchLine ou à aucun des deux. Vous êtes déjà confronté à de nombreuses 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, vous n'avez besoin de gérer que les enregistrements liés à SalesLine avec un StatusIssue de ReservPhysical ou ReservOrdered, toutes les autres combinaisons peuvent être ignorées pour l'instant, mais comme 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 pourrait ressembler à ceci pour l'instant :

  • MonProcesseur
    • MonProcesseur_Ventes
      • MonProcesseur_Ventes_RéserveCommandée
      • MonProcesseur_Ventes_RéservePhysique

Vous pouvez désormais facilement implémenter une méthode dans la super-classe qui instancie une sous-classe basée sur une énumération ModuleInventPurchSales et StatusIssue. Mais vous devrez alors modifier la super-classe à chaque fois que vous ajouterez 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 une nouvelle tâche par lots.

Vous pouvez également utiliser le framework SysExtension. Pour cela, vous devrez ajouter une autre classe, qui doit étendre SysAttribute. Cette classe sera utilisée comme attribut avec lequel vous pourrez 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, dans la mesure où elle comportera des membres de données et des méthodes parm pour obtenir et définir ces valeurs.

Dans notre cas, la ClassDeclaration pourrait ressembler à ceci :

class MyProcessorSystemAttribute extends SysAttribute
{
    ModuleInventPurchSales  module;
    StatusIssue             statusIssue;
    StatusReceipt           statusReceipt
}

Vous devez créer une méthode new() pour instancier tous les membres de données. Si vous le souhaitez, vous pouvez donner à certains ou à tous 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;
}

Et vous devez également implémenter une méthode parm pour chaque membre de données, mais je les ai omis ici car je suis sûr que vous savez comment le faire - sinon, considérons cela 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 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 ici est de décorer vos classes avec des attributs qui correspondent au type de traitement qu'elles effectuent. (Mais gardez à l'esprit qu'il existe des conventions de dénomination pour les hiérarchies de classes dans Dynamics AX et c'est toujours une bonne idée de les suivre, si possible).

Maintenant que vous avez décoré vos classes pour identifier le type de traitement que chacune d'elles effectue, vous pouvez profiter du framework SysExtension pour instancier les objets des sous-classes selon vos besoins.

Dans votre super classe (MyProcessor), vous pouvez ajouter une méthode de construction comme celle-ci :

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 en fait l'objet (pardonnez le jeu de mots) de tout cet article - est la méthode getClassFromSysAttribute() de la classe SysExtensionAppClassFactory. Cette méthode accepte le nom de la super classe d'une hiérarchie (et cette super classe n'a pas besoin d'être au sommet de la hiérarchie ; cela signifie simplement que seules les classes qui étendent cette classe seront éligibles) et un objet attribut.

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 validations ou de logiques supplémentaires que vous le souhaitez à la méthode de construction, 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 veillez à 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 des performances ? Je n'ai pas vraiment essayé de l'évaluer, mais j'ai le sentiment que les performances sont probablement moins bonnes que celles de la conception classique avec instruction switch. Cependant, étant donné que la plupart des problèmes de performances dans Dynamics AX sont causés par l'accès à la base de données, je ne m'en inquiéterais pas trop.

Bien sûr, si vous implémentez quelque chose qui nécessitera la création rapide de milliers d'objets, vous souhaiterez peut-être enquêter davantage, mais dans les cas classiques où vous instanciez simplement un seul objet pour effectuer un traitement long, je doute que cela ait de l'importance. De plus, compte tenu de mon conseil de dépannage (paragraphe suivant), il semble que le framework SysExtension s'appuie 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 de vider les caches sur le client et le serveur. Il ne devrait pas être nécessaire de redémarrer réellement l'AOS, mais cela peut être le dernier recours.

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

Mikkel Bang Christensen

A propos de l'auteur

Mikkel Bang 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.