BizTalk Mapper tips and tricks: How to reuse Scripting Functoids with Inline C# inside the same map

Posted: January 16, 2016  |  Categories: BizTalk Maps

In the last days, I’ve been migrating old maps from BizTalk Server 2004 to the last versions of BizTalk Server: 2013 and 2013 R2 and I have seen several “styles/approaches” to address mappings, some of them great, some of them not so much and other completely crazy.

Today I will address a topic that I, unfortunately, have seen constantly: Reusing Scripting Functoids with Inline C# inside the same map. This is also something that I address in my book: “BizTalk Mapping Patterns & Best Practices

Inline scripts are convenient for custom code that you are unlikely to use elsewhere in your application or other maps. In addition to being convenient for one-time scripts, inline scripts are also useful for declaring global variables for use among a number of scripts and to use several times inside the same map.

In general, some of the main reasons to use Scripting Functoid are:

  • Perform functions otherwise not available with built-in Functoids
  • It also allows you to perform complex logic transformations that are impossible to make with built-in Functoids.
  • To simplify the map, making it sometimes more easy to read, instead of using a complex Functoid chain
  • Or simply to optimize the transformation rules

However, you need to have some precautions when using, or reusing the same, Custom Inline C# scripts inside the Scripting Functoid:

  • Function Names Limitation
  • Compiler limitations
  • Reusability

Function Names Limitation

For each function/method inside the Scripting Functoid, you need to give different Method statements (name of the method + method parameters). If you have the same Method statement with different behaviors inside, for example:

  • In the first Scripting Functoid:
public string FormatDate(string inputDate, string inputFormat, string outputFormat)
{
    System.DateTime date;
    if (System.DateTime.TryParseExact(inputDate, inputFormat, 
        System.Globalization.CultureInfo.InvariantCulture, 
        System.Globalization.DateTimeStyles.AssumeLocal, out date))
    {
        return date.ToString(outputFormat);
    }
    return "";
}
  • And in the second Scripting Functoid:
public string FormatDate(string inputDate, string inputFormat, string outputFormat)
{
    if (String.IsNullOrEmpty(inputDate))
        return System.DateTime.Now.ToString(outputFormat);
    return inputDate;
}
different scripting functoid with the same name (not ok approach)

Because both methods have the name and the same number of parameters, the mapper will interpret has the same, regardless of the code inside the functions are the same or not. In this case, the first mapping rules to be reached is the first scripting functoid, which means that the second code inside the second functoid will be ignored and instead will be executed the exact same code that the first one, which is not what intended to do.

You can validate this map behavior if you validate the map and see the XSL produced:

public string FormatDate(string inputDate, string inputFormat, string outputFormat) 
{
 System.DateTime date;
 if (System.DateTime.TryParseExact(inputDate, inputFormat, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeLocal, out date))
 {
    return date.ToString(outputFormat);
 }
 return "";
} 

To fix this we need to give a different name to the second method, for example:

public string ValidateAndFormatDate(string inputDate, string inputFormat, string outputFormat)
{
    if (String.IsNullOrEmpty(inputDate))
        return System.DateTime.Now.ToString(outputFormat);
    return inputDate;
}

Nevertheless, you can give the same method name, if the method has a different number of inputs, for example, this case is a valid example:

  • In the first Scripting Functoid:
public string FormatDate(string inputDate, string inputFormat, string outputFormat)
{
    System.DateTime date;
    if (System.DateTime.TryParseExact(inputDate, inputFormat, 
        System.Globalization.CultureInfo.InvariantCulture, 
        System.Globalization.DateTimeStyles.AssumeLocal, out date))
    {
        return date.ToString(outputFormat);
    }
    return "";
}
  • And in the second Scripting Functoid:
public string FormatDate()
{
    return System.DateTime.Now.ToString();
}
Different-Scripting-Functoid-With-The-Same-Name-Ok

Once again if you validate the map to see the XSL produced, you can see that in this case both functions are being generated:

public string FormatDate(string inputDate, string inputFormat, string outputFormat) 
{
 System.DateTime date;
 if (System.DateTime.TryParseExact(inputDate, inputFormat, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeLocal, out date))
 {
   return date.ToString(outputFormat);
 }
 return "";
} 
public string FormatDate() 
{
 return System.DateTime.Now.ToString(); 
} 

Compiler limitations

When you create a script, for example, a C# function, remember to give it a correct name because all scripts with the same name are treated as one by the compiler regardless of whether the code is equal or not.

You also have to take into consideration that the BizTalk Mapper engine will save the inline scripts in the Extensible Stylesheet Language Transformations (XSLT) Stylesheet defining the map within the msxsl:script element. The following namespaces are supported by default: System, System.Collection, System.Text, System.Text.RegularExpressions, System.Xml, System.Xml.Xsl, System.Xml.Xpath, Microsoft.VisualBasic. You can add support for additional namespaces outside the listed using the namespace attribute of the child element, <msxsl:using>, of the <msxsl:script> element, but this will force us to use an External Custom XSLT file to accomplish the transformation. See more here: XSLT Stylesheet Scripting Using <msxsl:script> (https://docs.microsoft.com/en-us/dotnet/standard/data/xml/xslt-stylesheet-scripting-using-msxsl-script)

Reusability

Now let’s go to the main topic of this post Reusability of Scripting Functoids with Inline C# inside the same map.

What I normally see is people doing two things:

  • Copy and paste the entire method to all the Scripting Functoids – I’m calling that: Reusing in a Bad Way
    • In this case, both scripting functoids have the exact same code
public string FormatDate(string inputDate, string inputFormat, string outputFormat)
{
    System.DateTime date;
    if (System.DateTime.TryParseExact(inputDate, inputFormat, 
        System.Globalization.CultureInfo.InvariantCulture, 
        System.Globalization.DateTimeStyles.AssumeLocal, out date))
    {
        return date.ToString(outputFormat);
    }
    return "";
}
Reuse Scripting Functoid in a Bad Way

This is a very simple case, now imagine a map with several grid pages and several functoids. The problem is this approach, as we explained earlier, is that if we for some reason change the code of one the scripting functoids, that is not the first to be translated by the mapper engine, the changes will not take effect!!!

  • Copy and paste the entire method to all the Scripting Functoids and give it a different name! – I’m calling that: Reusing in a Crazy Way
    • In this case, the first scripting functoids have:
public string FormatDate(string inputDate, string inputFormat, string outputFormat)
{
    System.DateTime date;
    if (System.DateTime.TryParseExact(inputDate, inputFormat, 
        System.Globalization.CultureInfo.InvariantCulture, 
        System.Globalization.DateTimeStyles.AssumeLocal, out date))
    {
        return date.ToString(outputFormat);
    }
    return "";
}
  • And the second one has:
public string FormatDate2(string inputDate, string inputFormat, string outputFormat)
{
    System.DateTime date;
    if (System.DateTime.TryParseExact(inputDate, inputFormat, 
        System.Globalization.CultureInfo.InvariantCulture, 
        System.Globalization.DateTimeStyles.AssumeLocal, out date))
    {
        return date.ToString(outputFormat);
    }
    return "";
}

The one difference is that the first one is called FormatDate and the second FormatDate2. I’m not joking, I saw this approach being used nearly 8 times (Func1, Func2, Func3, Func4, … Func8) on a map! Of course, this solves the problem of the first approach but now you have another! The artifact size deployed will be larger, not critical, but if you can avoid is better. This will happen because in this case all methods will be embedded in the XSL file that will be deployed to your environment.

public string FormatDate(string inputDate, string inputFormat, string outputFormat) 
{
 System.DateTime date;
 if (System.DateTime.TryParseExact(inputDate, inputFormat, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeLocal, out date))
 {
    return date.ToString(outputFormat);
 }
 return ""; 
} 
public string FormatDate2(string inputDate, string inputFormat, string outputFormat) 
{
 System.DateTime date;
 if (System.DateTime.TryParseExact(inputDate, inputFormat, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeLocal, out date))
 {
    return date.ToString(outputFormat);
 }
 return ""; 
} 

Another problem, even worse, of this approach, is that if you need to make some changes in the method… you need to do the same changes in every single functoid! If you want them to work in a coherent and transparent way.

So what is the best way to reuse Scripting Functoids with inline C#?

Easy! You need to remember that when you have two or more Custom Inline C# scripts with the same Method statement (name of the method + method parameters), the compile will only take into consideration the first one linked in the map, even if the others have a different code inside they are ignored by the compiler.

Reuse Scripting Functoid in a Good Way

When we use Custom Inline C# scripts the best way to implement reusability is to specify the body function in only one Scripting Functoid and the remaining ones specify only the function declaration, in this sample in the first Scripting Functoid will have the following code:

public string FormatDate(string inputDate, string inputFormat, string outputFormat)
{
    System.DateTime date;
    if (System.DateTime.TryParseExact(inputDate, inputFormat, 
        System.Globalization.CultureInfo.InvariantCulture, 
        System.Globalization.DateTimeStyles.AssumeLocal, out date))
    {
        return date.ToString(outputFormat);
    }
    return "";
}

And the second one is only the following declaration:

public string FormatDate(string inputDate, string inputFormat, string outputFormat)

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

Download

THIS SAMPLE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND.

You can download BizTalk Mapper Tips and Tricks: How to create a Global Inline C# Function from GitHub here:

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.

5 thoughts on “BizTalk Mapper tips and tricks: How to reuse Scripting Functoids with Inline C# inside the same map”

  1. Let me use it now, I have similar requirement in the current project. Till now I was doing in the crazy way !

  2. Once again great tidbit – thx Sandro, hope you are doing well (had to laugh when I saw the subtitle, I was refactoing some old BTS 2004 maps about 3 years back and what a pain at times)

  3. Hi Sandro. I’m trying to implement this, but on the second functoid I am getting an exception “Inline Script Error: ; expected”, as if it doesn’t realize I’m trying to use the same method statement. The statements do match between the two functoids. I can’t see any differences between my map and yours, but I am able to compile and test with yours successfully. Any suggestions?

Leave a Reply

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

turbo360

Back to Top