Imbriquer des boucles dans une map à double entrée

Hao WU
Publié par Hao WU
Catégorie : BizTalk
12/11/2019

CONTEXTE

Récemment, en voulant appliquer des conditions sur une map à double entrée lorsque les 2 messages sources ont des nœuds répétables, je me suis rendue compte que ce n’était pas si évident.

 

Situation

Le message B (qui provient de la réponse d’une requête SQL) correspond à une liste de commandes. Le message A quant à lui, correspond à une nouvelle liste de commandes. Ces deux messages sont bien des messages différents (2 modélisations xsd distinctes).

 

Besoin

Pour chaque commande dans le message A, il s’agit de parcourir la nouvelle liste de commandes en appliquant la règle suivante : si la commande n’est pas présente dans la nouvelle liste, alors il faut la supprimer (plus d’autres étapes non pertinentes ici).
Le problème survient lors de l’utilisation du fonctoid Looping : en faisant une boucle imbriquée sur les 2 messages, le comportement du mapper BizTalk n’est pas celui souhaité. Ce dernier réalise 2 boucles séparément sur le message sortant et ignore totalement notre volonté de faire 2 boucles imbriquées.

 

DESCRIPTION D’UN CAS EXEMPLE

 

Prenons un cas simplifié : ci-dessous une liste de clients, avec le nœud Client répétable.

 

Puis ci-dessous une liste de commandes, avec le nœud Commande répétable.

 

 

Le lien entre ces 2 messages est le champ NomClient. La règle est la suivante : pour chaque commande, si le champ NomClient existe bien dans la liste des clients, la commande doit être facturée.

Ci-dessous le schéma sortant qui modélise la liste de Factures :

 

 

Pour le montant de la facture, nous ferons juste le produit de PrixUnitaire et Quantite.
Intuitivement, nous avons envie d’utiliser directement le fonctoid Looping pour parcourir nos nœuds répétables. Commençons par faire 2 boucles sur les 2 schémas entrants et analysons le comportement du mapper.

 

 

Ci-dessous le XSLT généré par la map :

 

<xsl:template match="/s2:Root">
  <ns0:Factures>
    <xsl:for-each select="InputMessagePart_1/s0:Commandes/Commande">
      <Facture />
    </xsl:for-each>
    <xsl:for-each select="InputMessagePart_0/s1:Clients/Client">
      <Facture />
    </xsl:for-each>
  </ns0:Factures>
</xsl:template>

 

Le XSLT généré indique que nous aurons autant de factures que la somme des commandes et des clients. Dans cette solution, il n’y a donc pas du tout de notion de boucle imbriquée.
2 solutions pour répondre à cette problématique :

Solution 1 : avec du XSLT

Comme vous pouvez l’imaginer, nous pouvons bien sûr nous en sortir avec du XSLT :

<xsl:element name="ns0:Factures" xmlns:ns0="http://FacturerCommandes.Schemas.ListeFactures">
  <xsl:for-each select="/*[local-name()='Root']/*[local-name()='InputMessagePart_1']/*[local-name()='Commandes']/*[local-name()='Commande']">
  <xsl:element name="Facture">
    <xsl:variable name="NomClientCommande" select="string(./NomClient)" />
    <xsl:variable name="trouveClient" select="count(/*[local-name()='Root']/*[local-name()='InputMessagePart_0']/*[local-name()='Clients']/*[local-name()='Client'][*[local-name()='NomClient' and text()=$NomClientCommande]])" />
    <xsl:if test="$trouveClient!=number(0)">
      <xsl:element name="NomClient">
        <xsl:value-of select="$NomClientCommande" />
      </xsl:element>
      <xsl:element name="Montant">
        <xsl:variable name="Quantite" select="string(./Quantite)" />
        <xsl:variable name="PrixUni" select="string(./PrixUnitaire)" />
        <xsl:value-of select="number($Quantite)*number($PrixUni)" />
      </xsl:element>
    </xsl:if>
  </xsl:element>
  </xsl:for-each>
</xsl:element>

 

Comme pouvez le constater, il s’agit de parcourir notre liste de commandes et de chercher si son client se trouve dans le message de la liste des clients. Aussi simple que cela.

 

Solution 2 : avec les fonctoids

 

Cette solution est un peu moins intuitive à mettre en place mais s’avère bien pratique dans le cas où vous ne pouvez pas réaliser votre map en full XSLT (si la map est déjà existante ou si le schéma destination est très complexe par exemple).
Voici à quoi ressemble la map :

Tout d’abord, il s’agit de mettre en place une boucle sur la liste des commandes (cf. fonctoid Looping). Ensuite, l’idée principale de cette solution réside dans l’utilisation du fonctoid Cumulative Concatenate. En effet, ce fonctoid va nous permettre de boucler sur la liste des clients. Puis, nous utiliserons un script afin de rechercher si la valeur du champ NomClient du message Commandes se trouve dans la liste des clients.
Voici le script en question :

 public static bool CheckClient(string NomClient, string ListeEnClients)
{
    string[] vals = ListeEnClients.Split(',');
    return vals.Contains(NomClient);
}

 

Résultat

Supposons notre message entrant (à double entrée) comme suit :

Ainsi, pour les 2 solutions, nous obtenons le résultat suivant :

 

En espérant que les 2 solutions proposées dans cet article vous seront utiles.