BizTalk Mapper tips and tricks: How to properly implement conditions using Functoids chains

  • Sandro Pereira
  • Jan 27, 2016
  • 8 min read

Common Pitfall: Conditional Logic in BizTalk Maps

I’ve seen this scenario happen many times: developers apply conditional logic (if‑then‑else) in BizTalk maps by building complex functoid chains.

At first glance, this approach seems perfectly reasonable. After all, implementing conditions with functoids is a common and often trivial task. However, in practice, we frequently implement these conditions incorrectly or inefficiently.

Understanding the BizTalk Mapping Engine

I have already explained the BizTalk mapping processing model in my book, BizTalk Mapping Patterns and Best Practices. Still, it is always worth revisiting this topic, as it is essential to understanding why conditional logic sometimes behaves unexpectedly.

BizTalk maps follow a very specific execution model:

  • The BizTalk mapping engine traverses the destination schema from beginning to end.
  • Mapping rules are constructed and executed as links are encountered in the destination schema.
  • Source data is extracted only when a corresponding destination link is reached.

Why Conditions Often Behave Unexpectedly

This is the critical part that many developers overlook.

When the BizTalk mapper engine reaches a condition rule, it must first translate (execute) all operations downstream of that condition. Only after executing those downstream operations does BizTalk evaluate the condition itself.

As a result, even when a condition evaluates to false, BizTalk may still execute functoids connected further down the chain. This behavior often leads to unnecessary processing, unexpected results, or performance issues.

📝 One-Minute Brief

This post explains how to correctly implement conditional logic (if‑then‑else) in BizTalk maps using functoid chains. It highlights common mistakes, explains how the BizTalk mapping engine processes rules, and shows how improper functoid chaining can lead to unexpected behavior during transformations.

Let’s look at the following example:

Complex Functoid Chain Condition wrong

Where the basic rule that we want to implement is:

  • If the operation is equal to insert:
    • Then we need to set, in the Cross Referencing tables, and return an identifier (Set Common ID Functoid), based on some content from the source schema, and in some values present or not in the Cross Referencing tables (Get Common ID Functoid), and mapped to the CommonId element in the destination schema.
  • If the operation is different from insert:
    • Then we need to retrieve an identifier from the Cross Referencing tables (Get Common ID Functoid), again based on some content from the source schema, and map the result to the CommonId element in the destination schema.

So, to be simple and clear, what the BizTalk mapper engine is doing in the picture above is:

  • It found 2 rules to translate because we have two links connected with the CommonId element
  • In the first rule, it will: If the operation is equal to insert:
    • Get one element from the “keys” record in the source schema and put it into a variable
    • Try to retrieve two identifiers, based on some content from the source schema or static data.
    • From the identifiers retrieved earlier, it applies a transformation rule (not important for this demo).
    • Set and return an identifier, based on the content of:
      • one element from the keys.
      • and the transformation rule, associated with the earlier retrieval of identifiers.
    • And finally, will check if the operation is equal to insert:
      • Then, map the identifier created in the previous step to the element CommonId.
  • In the second rule, it will: If the operation is different from insert:
    • Try to retrieve two identifiers, based on some content from the source schema or static data.
    • From the identifiers retrieved earlier, it applies a transformation rule (not important for this demo).
    • And finally, will check if the operation is different to insert:
      • Then, map the identifier created in the previous step to the element CommonId.

Are you already seeing the problem with this sample?

Let’s analyze the generated XSLT code and try to see if it makes more sense and detect the problem in a clear way:

<xsl:variable name="var:v14" select="userCSharp:LogicalNe(&quot;insert&quot; , string(@operation))" />
<xsl:variable name="var:v23" select="userCSharp:LogicalEq(&quot;insert&quot; , $var:v22)" />
<xsl:variable name="var:v18" select="ScriptNS1:GetCommonID(&quot;INPUT1&quot; , &quot;INPUT2&quot; , string($var:v17))" />
<xsl:variable name="var:v19" select="ScriptNS1:GetCommonID(&quot;INPUT1&quot; , &quot;INPUT3&quot; , string(keys/myelement/text()))" />
<xsl:variable name="var:v15" select="ScriptNS0:GetValue()" />
<xsl:variable name="var:v20" select="ScriptNS2:GetValue(string($var:v15) , string($var:v18) , string($var:v19))" />
 
<xsl:if test="string($var:v14)='true'">
     <xsl:variable name="var:v21" select="string($var:v20)" />
     <btsCommonId>
            <xsl:value-of select="$var:v21" />
     </btsCommonId>
</xsl:if>
 
<xsl:variable name="var:v25" select="ScriptNS1:SetCommonID(&quot;FamilyMemberVehicle&quot; , &quot;CRM&quot; , $var:v24 , string($var:v20))" />
 
<xsl:if test="string($var:v23)='true'">
     <xsl:variable name="var:v26" select="string($var:v25)" />
     <btsCommonId>
            <xsl:value-of select="$var:v26" />
     </btsCommonId>
</xsl:if>

And now, do you see the problem with this sample?

The problem is that, independent of the type of operation, whether it is an insert or other operation, it will always set an identifier in the Cross Referencing tables! (which, by the way, in my scenario, will induce problems – violate key). And this happens because the condition was defined at the end of the rule (in the right corner).

Complex Functoid Chain Condition problem

How you should read the rules

Important Note: basically, there are some exceptions; you always need to read the rules:

  • from top to bottom in the order that they are happier in the destination schema.
  • and from the left to the right (from the source schema to the destination schema) for a specific link connected to an element, field, or record in the destination schema.

So, in this case, the solution here is very simple: we need to move the condition to a position further downstream, which will allow it to be executed sooner, especially when the condition is equal to insert, because the second part (get two identifiers from the Cross Referencing tables) is common for both rules (then and else).

In this case, we need to place the condition before we execute the set and return an identifier from the Cross Referencing tables operation, as the picture below shows:

Complex Functoid Chain Condition right

If we check the XSLT code once again, now we will notice that:

  • We are doing all the common operations before the condition rule is executed.
  • And for each particular scenario, we will map the result (not equal to “insert”) or apply more operations and map the result (equal to insert).
<xsl:variable name="var:v14" select="userCSharp:LogicalNe(&quot;insert&quot; , string(@operation))" />
<xsl:variable name="var:v18" select="ScriptNS1:GetCommonID(&quot;INPUT1&quot; , &quot;INPUT2&quot; , string($var:v17))" />
<xsl:variable name="var:v19" select="ScriptNS1:GetCommonID(&quot;INPUT1&quot; , &quot;INPUT3&quot; , string(keys/myelement/text()))" />
<xsl:variable name="var:v15" select="ScriptNS0:GetValue()" />
<xsl:variable name="var:v20" select="ScriptNS2:GetValue(string($var:v15) , string($var:v18) , string($var:v19))" />
<xsl:variable name="var:v23" select="userCSharp:LogicalEq(&quot;insert&quot; , $var:v22)" />
 
<xsl:if test="string($var:v14)='true'">
     <xsl:variable name="var:v21" select="string($var:v20)" />
     <btsCommonId>
            <xsl:value-of select="$var:v21" />
     </btsCommonId>
</xsl:if>
 
<xsl:if test="string($var:v23)='true'">
     <xsl:variable name="var:v24" select="string($var:v20)" />
     <xsl:variable name="var:v25" select="string(keys/siva_viaturaagregadoid/text())" />
     <xsl:variable name="var:v26" select="ScriptNS1:SetCommonID(&quot;FamilyMemberVehicle&quot; , &quot;CRM&quot; , $var:v25 , string($var:v24))" />
     <btsCommonId>
            <xsl:value-of select="$var:v26" />
     </btsCommonId>
</xsl:if>

Like this scenario, there are several, more or less complex, more or less critical. Some of them we don’t “even notice” because everything works well, but if we check carefully, we may sometimes make several unnecessary operations that can induce, or may induce, performance problems in our transformations.

Good practices

As a reference and good practice, when you are implementing conditions using Functoid chains, you should make the conditions as early as possible, i.e., put the conditions (if-then-else) as far to the left as possible of the Functoid chain.

This will allow you:

  • To group and execute certain tasks only in the context of the condition (inside the <xsl:if> statement).
  • And better performance because we are reducing the “noise” produced in the XSLT code generated by the compiler.

Easy, isn’t it?
Hope you enjoy it.

Hope you find this helpful! If you liked the content or found it useful and would like to support me in writing more, consider buying (or helping to buy) a Star Wars Lego set for my son. 

Thanks for Buying me a coffe
Author: Sandro Pereira

Sandro Pereira lives in Portugal and works as a consultant at DevScope. In the past years, he has been working on implementing Integration scenarios both on-premises and cloud for various clients, each with different scenarios from a technical point of view, size, and criticality, using Microsoft Azure, Microsoft BizTalk Server and different technologies like AS2, EDI, RosettaNet, SAP, TIBCO etc. He is a regular blogger, international speaker, and technical reviewer of several BizTalk books all focused on Integration. He is also the author of the book “BizTalk Mapping Patterns & Best Practices”. He has been awarded MVP since 2011 for his contributions to the integration community.

Leave a Reply

Your email address will not be published. Required fields are marked *

The Ultimate Cloud
Management Platform for Azure

Supercharge your Azure Cost Saving

Learn More
Turbo360 Widget

Back to Top