Why is so hard to make a simple If-Then-Else Functoid? … well, not anymore!

  • Sandro Pereira
  • Feb 10, 2016
  • 13 min read

Sometimes I ask myself: Why is it so hard to make a simple If-Then-Else Functoid, or even so painful to do an If-Then-Else operation, using BizTalk mapper?

I don’t mean to say that it is complicated, quite the opposite is quite easy to make If…Then…Else statements using the Mapper. You can use If…Then…Else statements, to be completely correct, you can use something related to an If…Then…Else statements, to execute blocks of statements depending on the Boolean value of a condition, by normally using:

  • One Logical Functoid (Logical Existence, Logical String, Logical Numeric, Equal, Greater Than, Less Than, and so on) to determine:
    • whether the Record, Field Element, or Field Attribute node that is linked to it exists or has a valid value for a particular input instance message.
    • or if a condition matches.
  • One Logical NOT Functoid to negate the Logical Functoid.
  • And two Value Mapping Functoids to return the value that we want to link based on the result of the condition.
BizTalk out of the box if-then-else condition

📝 One-Minute Brief

This post explains why implementing If‑Then‑Else logic in BizTalk maps is often more complex than expected. It breaks down how conditional logic is typically built using functoid chains, explains the underlying execution model, and introduces a cleaner and more efficient approach to handle conditions in BizTalk Mapper.

In this sample, if the Operation element is equal to Create:

  • Then, you will map the value of the element ValueA from the source schema to the element Result in the destination schema.
  • Else (otherwise): you will map the value of the element ValueB from the source schema to the element Result in the destination schema.

So, this functoid chain will provide a secondary path of execution when an if clause evaluates to false, the Else path. I said earlier: “something related to an If…Then…Else statement” because, programming speaking, If…Then…Else  statements are implemented by:

//C#
if(condition)
{
    //something if true
}
else
{
    //something if false
}

Or

<!-- XSLT -->
<xsl:choose>
 <xsl:when test="expression">
    ... something if true ...
 </xsl:when>
  <xsl:otherwise>
    ... something if false ...
  </xsl:otherwise>
</xsl:choose>

But in fact, what this functoid chain does is two if statements:

<!-- something if true -->
<xsl:if test="string($var:v1)='true'">
 <xsl:variable name="var:v2" select="ValueA/text()" />
  <Result>
    <xsl:value-of select="$var:v2" />
  </Result>
</xsl:if>
<!-- something if false (var:v3 contains the negation of var:v1) -->
<xsl:if test="string($var:v3)='true'">
  <xsl:variable name="var:v4" select="ValueB/text()" />
 <Result>
    <xsl:value-of select="$var:v4" />
  </Result>
</xsl:if>

This approach is good for small messages, but even with small messages, the Mapper will add a lot of unnecessary code and operations. Because of that, I decided to create an If-Then-Else Functoid to use in such situations and improve the map’s performance. Of course, undoubtedly, the best option and with the most performance to make conditions is using custom XSLT code (but this is another story).

The annoying things in using these out-of-the-box functoids are:

  • We get these  annoying warnings (but they are useful in many scenarios) when we validate the maps:
    • warning btm1004: The destination node “Result” has multiple inputs. For a destination node to have multiple inputs, one of its ancestors should be connected to a looping functoid.
  • And for a simple if-then-else operation, we need to use a minimum of 4 functoids. If we have several conditions inside the map, it will be easily filled with functoids, and as a result, it will become a little confused and sometimes difficult to manage.
  • And, obviously, we will have several unnecessary operations.

So, why is it so hard to make an If-Then-Else Functoid?

What I want to achieve is create a custom functoid that accepts 3 inputs:

  • A Boolean – the result of a previous Logical Functoid (Logical Existence, Logical String, Logical Numeric, Equal, Greater Than, Less Than, and so on).
  • And two inputs.

The custom Functoid returns a value based on a condition, using one of two input parameters:

  • If the condition (first input) is True, then the value of the second input parameter is returned.
  • Otherwise, the Third input is returned.

Translating to C# code, it will be something like this:

public string IfThenElseOperation(string condition, string trueValue, string falseValue)
{
    if (System.Convert.ToBoolean(condition))
        return trueValue;
    return falseValue;
}

Note: I will not address this topic here; this is content deserved a completely different post and is dedicated to the topic, but the best option that you have is creating a custom functoid belonging to the String functoid category (if you like it, put a comment in the post and I will address this topic at another time/post).

However, if you do that, you will find that out-of-the-box, it is impossible to create a custom functoid based on a Logical Functoid, and the reason why this is true is that all Logical Functoids available out-of-the-box with BizTalk only accept the following output connection types:

  • ConnectionType.Element
  • ConnectionType.FunctoidAssert
  • ConnectionType.FunctoidNilValue
  • ConnectionType.FunctoidKeyMatch
  • ConnectionType.FunctoidTableLooping
  • ConnectionType.FunctoidValueMapping
  • ConnectionType.FunctoidScripter
  • ConnectionType.FunctoidLogical;

ConnectionType Enumeration: Specifies the types of connections that can be used as inputs or outputs for a functoid. This enumeration has a FlagsAttribute attribute that allows a bitwise combination of its member values.

ConnectionType.FunctoidString is not allowed in all the existing Logical Functoids. And that is the reason why you will find it impossible to create a custom functoid based on a Logical functoid (don’t know the reason why Microsoft decided to implement this limitation)

#1 Azure Monitoring Platform

How did I solve (or overcome) this limitation?

To solve (workaround) this limitation, so that I could create and use a custom if-then-else functoid, at the same time be fully compatible with existing functoids and not produce any additional code, I was forced to create my own personal custom Logical Functoids:

  • Advance Logical AND Functoid
  • Advance Equal Functoid
  • Advance Greater Than Functoid
  • Advance Greater Than or Equal To Functoid
  • Advance Less Than Functoid
  • Advance Less Than or Equal To Functoid
  • Advance Not Equal Functoid
  • Advance Logical NOT Functoid
  • Advance Logical OR Functoid
Toolbox with custom logical functoids

That has the same behavior as the out-of-the-box Logical Functoids:

  • Logical AND Functoid
  • Equal Functoid
  • Greater Than Functoid
  • Greater Than or Equal To Functoid
  • Less Than Functoid
  • Less Than or Equal To Functoid
  • Not Equal Functoid
  • Logical NOT Functoid
  • Logical OR Functoid

It also has the advantage that it accepts the String output connection type.

base.OutputConnectionType = ConnectionType.FunctoidString |
 ConnectionType.Element | ConnectionType.FunctoidAssert |
 ConnectionType.FunctoidNilValue | ConnectionType.FunctoidKeyMatch |
 ConnectionType.FunctoidTableLooping |
 ConnectionType.FunctoidValueMapping |
 ConnectionType.FunctoidScripter | ConnectionType.FunctoidLogical;

By doing that, I can create my if-then-else custom functoid:

namespace BizTalk.CustomAdvanced.Functoids
{
    [Serializable]
    public class IfThenElse : BaseFunctoid
    {
        public IfThenElse()
            : base()
        {
            //ID for this functoid
            this.ID = 10900;

            // resource assembly must be ProjectName.ResourceName if building with VS.Net
            SetupResourceAssembly("BizTalk.Logical.Functoids.LogicalResources", Assembly.GetExecutingAssembly());

            //Setup the Name, ToolTip, Help Description, and the Bitmap for this functoid
            SetName("IDS_IFELSEFUNCTOID_NAME");
            SetTooltip("IDS_IFELSEFUNCTOID_TOOLTIP");
            SetDescription("IDS_IFELSEFUNCTOID_DESCRIPTION");
            SetBitmap("IDS_IFELSEFUNCTOID_BITMAP");

            //category for this functoid. This functoid goes under the String Functoid Tab in the
            this.Category = FunctoidCategory.String;

            // Set the limits for the number of input parameters. This example: 1 parameter
            this.SetMinParams(3);
            this.SetMaxParams(3);

            // Add one line of code as set out below for each input param. For multiple input params, each line would be identical.
            this.AddInputConnectionType(ConnectionType.AllExceptRecord); //first input
            this.AddInputConnectionType(ConnectionType.AllExceptRecord); //Second input
            this.AddInputConnectionType(ConnectionType.AllExceptRecord); //Third input

            // The functoid output can go to any node type.
            this.OutputConnectionType = ConnectionType.AllExceptRecord;

            SetScriptBuffer(ScriptType.CSharp, this.GetCSharpBuffer());
            HasSideEffects = false;
        }

        private string GetCSharpBuffer()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("public string IfThenElseOperation(string condition, string trueValue, string falseValue)\n");
            builder.Append("{\n");
            builder.Append("\tif (System.Convert.ToBoolean(condition))\n");
            builder.Append("\t\treturn trueValue;\n");
            builder.Append("\treturn falseValue;\n");
            builder.Append("}\n");
            return builder.ToString();
        }
    }
}

And have the ability to connect my logical functoids to a custom String functoid. Of course, you need to use it wisely!

BizTalk custom if-then-else condition

The beauty of this approach is that:

  • We reduce the functoids chain. Using fewer functoids simplifies the map visually, which is very useful when you have many conditions, even if they are simple.
  • We will improve the map performance a little more, compared to the use of out-of-the-box functoids
    • Using Custom If-Then-Else Functoid:
<xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(Operation/text()) , 'Create')" />
<xsl:variable name="var:v2" select="userCSharp:IfThenElseOperation(string($var:v1) , string(ValueA/text()) , string(ValueB/text()))" />
 
   <ns0:Output>
      <Result>
        <xsl:value-of select="$var:v2" />
      </Result>;
    </ns0:Output>
 
public bool LogicalEq(string val1, string val2)
{
    bool ret = false;
    double d1 = 0;
    double d2 = 0;
    if (IsNumeric(val1, ref d1) && IsNumeric(val2, ref d2))
    {
        ret = d1 == d2;
    }
    else
    {
        ret = String.Compare(val1, val2, StringComparison.Ordinal) == 0;
    }
    return ret;
}
 
public string IfThenElseOperation(string condition, string trueValue, string falseValue)
{
    if (System.Convert.ToBoolean(condition))
        return trueValue;
    return falseValue;
}
 
public bool IsNumeric(string val)
{
    if (val == null)
    {
        return false;
    }
    double d = 0;
    return Double.TryParse(val, System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d);
}
 
public bool IsNumeric(string val, ref double d)
{
    if (val == null)
    {
        return false;
    }
    return Double.TryParse(val, System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d);
}
  • Using Out-of-the-box Functoids:
<xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(Operation/text()) , 'Create')" >
<xsl:variable name="var:v3" select="userCSharp:LogicalNot(string($var:v1))" >
 
   <ns0:Output>
      <xsl:if test="string($var:v1)='true'">
       <xsl:variable name="var:v2" select="ValueA/text()" />
       <Result>
         <xsl:value-of select="$var:v2" />
       </Result>
     </xsl:if>
      <xsl:if test="string($var:v3)='true'">
        <xsl:variable name="var:v4" select="ValueB/text()" />
        <Result>
          <xsl:value-of select="$var:v4" />
       <Result>
      <xsl:if>
    <ns0:Output>
   
public bool LogicalEq(string val1, string val2)
{
    bool ret = false;
    double d1 = 0;
    double d2 = 0;
    if (IsNumeric(val1, ref d1) && IsNumeric(val2, ref d2))
    {
        ret = d1 == d2;
    }
    else
    {
        ret = String.Compare(val1, val2, StringComparison.Ordinal) == 0;
    }
    return ret;
}
 
public bool LogicalNot(string val)
{
    return !ValToBool(val);
}
 
public bool IsNumeric(string val)
{
    if (val == null)
    {
        return false;
    }
    double d = 0;
    return Double.TryParse(val, System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d);
}
 
public bool IsNumeric(string val, ref double d)
{
    if (val == null)
    {
        return false;
    }
    return Double.TryParse(val, System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d);
}
 
public bool ValToBool(string val)
{
    if (val != null)
    {
        if (string.Compare(val, bool.TrueString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return true;
        }
        if (string.Compare(val, bool.FalseString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return false;
        }
        val = val.Trim();
        if (string.Compare(val, bool.TrueString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return true;
        }
        if (string.Compare(val, bool.FalseString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return false;
        }
        double d = 0;
        if (IsNumeric(val, ref d))
        {
            return (d > 0);
        }
    }
    return false;
}
  • You don’t need to deploy any of these custom functoids (advanced logical functoids or/and if-the-else functoid) to your production environment; you only need to have them in your development environment to be used inside Visual Studio because all of them are custom inline functoids, i.e., they will add the necessary code inside the XSLT file.
  • And finally, you can use all the custom advanced Logical Functoids alongside the out-of-the-box Logical Functoids without any impact and without producing additional code, because they will produce the exact same code as the out-of-the-box Logical Functoids!
BizTalk custom and out-of-the-box if-then-else condition side-by-side

Generated code:

<!-- Custom Functoid -->;
<xsl:variable name="var:v1" select="userCSharp:LogicalEq(string(Operation/text()) , 'Create')" />;
<xsl:variable name="var:v2" select="userCSharp:IfThenElseOperation(string($var:v1) , string(ValueA/text()) , string(ValueB/text()))" />;
 
<!-- out-of-the-box Functoids -->;
<xsl:variable name="var:v3" select="string(Operation/text())" />;
<xsl:variable name="var:v4" select="userCSharp:LogicalEq($var:v3 , 'Create')" />;
<xsl:variable name="var:v6" select="userCSharp:LogicalNot(string($var:v4))" />;
 
   <ns0:Output>;
<!-- Custom Functoid -->;
      <Result>;
        <xsl:value-of select="$var:v2" />;
      </Result>;
<!-- out-of-the-box Functoids -->;
      <xsl:if test="string($var:v4)='true'">;
       <xsl:variable name="var:v5" select="ValueA/text()" />;
        <Total>;
          <xsl:value-of select="$var:v5" />;
        </Total>;
      </xsl:if>;
      <xsl:if test="string($var:v6)='true'">;
        <xsl:variable name="var:v7" select="ValueB/text()" />;
        <Total>;
          <xsl:value-of select="$var:v7" />;
        </Total>;
      </xsl:if>;
    </ns0:Output>;
  </xsl:template>;
 
<!-- Both out-of-the-box Functoids and custom functoids will produce this LogicalEq code! -->;
public bool LogicalEq(string val1, string val2)
{
    bool ret = false;
    double d1 = 0;
    double d2 = 0;
    if (IsNumeric(val1, ref d1) && IsNumeric(val2, ref d2))
    {
        ret = d1 == d2;
    }
    else
    {
        ret = String.Compare(val1, val2, StringComparison.Ordinal) == 0;
    }
    return ret;
}
 
public string IfThenElseOperation(string condition, string trueValue, string falseValue)
{
    if (System.Convert.ToBoolean(condition))
        return trueValue;
    return falseValue;
}
 
public bool LogicalNot(string val)
{
    return !ValToBool(val);
}
 
public bool IsNumeric(string val)
{
    if (val == null)
    {
        return false;
    }
    double d = 0;
    return Double.TryParse(val, System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d);
}
 
public bool IsNumeric(string val, ref double d)
{
    if (val == null)
    {
        return false;
    }
    return Double.TryParse(val, System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d);
}
 
public bool ValToBool(string val)
{
    if (val != null)
    {
        if (string.Compare(val, bool.TrueString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return true;
        }
        if (string.Compare(val, bool.FalseString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return false;
        }
        val = val.Trim();
        if (string.Compare(val, bool.TrueString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return true;
        }
        if (string.Compare(val, bool.FalseString, StringComparison.OrdinalIgnoreCase) == 0)
        {
            return false;
        }
        double d = 0;
        if (IsNumeric(val, ref d))
        {
            return (d > 0);
        }
    }
    return false;
}

It may not be the perfect solution, I agree with that, but I found a very interesting and useful approach for many scenarios. I hope you enjoy it because I have been using it a lot lately 🙂

BizTalk custom and out-of-the-box if-then-else condition side-by-side

The functoids will be available in the next version of BizTalk Mapper Extensions UtilityPack; until then, you have all the source code and samples here:

Download

You can download the source code from GitHub:

For those looking to move beyond the basics of message transformation, I highly recommend checking out my eBook, BizTalk Mapping Patterns & Best Practices, published in partnership with BizTalk360.

This resource is a deep dive into the real-world challenges of data transformation. It covers:

  • Mapping Patterns: From simple field-to-field links to complex structural shifts.
  • Performance Optimization: How to build maps that don’t slow down your environment.
  • XSLT vs. Functoids: Knowing exactly when to use built-in tools and when to write custom code.

Whether you are maintaining a legacy BizTalk 2010 environment or planning a migration, these patterns are the foundation of clean, maintainable integration.

Download the full eBook here: BizTalk Mapping Patterns & Best Practices

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