global c# functions

Thinking outside the box (or not): How to create “Global C# function” to be reused inside a map?

Published on : Jan 17, 2017

Category : BizTalk Server

Sandro

Author

The Scripting functoid enables you to use Inline Custom Script (C# .NET, JScript .NET, Visual Basic .NET, Extensible Stylesheet Language Transformations (XSLT) or XSLT Call Template) or call code from an external assembly at run time to perform operation otherwise not available through built-in Functoids. For example, using Scripting functoid with Inline C# is convenient for creating a custom operation that you are unlikely to use elsewhere in your application or other maps.

You can download the entire article as a PDF document.
How to create “Global C# function” to be reused inside a map?.

Pros and Cons of using the Scripting Functoid

In one of my previous posts, “BizTalk Mapper tips and tricks: How to reuse Scripting Functoids with Inline C# inside the same map”, I addressed and explained the main reasons why developers should use the Scripting Functoid, being:

  • Executing functions otherwise not available with built-in Functoids
  • Allowing you to perform complex logic transformations that are impossible to make with built-in Functoids
  • Simplifying the map and making it sometimes more easy to read, instead of using a complex Functoid chain
  • Optimizing the transformation rules

There are also some precautions you need to take care of when using, or reusing the same Custom Inline C# scripts inside the Scripting Functoid, namely:

  • Function Names Limitation
  • Compiler limitations
  • Reusability

Developers use several unusual approaches to accomplish the reuse of Scripting Functoids inside the BizTalk Mapper, approaches that I call “Reusing in a Bad Way” and “Reusing in a Crazy Way”. At the end of the post, I explained which was the best way to reuse Scripting Functoids with inline C# using a very simple scenario.

Reusing Scripting Functoids

In my opinion, the solution provided in my post is still the best option. However, it can also provide some challenges in terms of maintenance and development, that I will be addressing in this article.

Let’s imagine a scenario identical to what I used in the previous post, but with some differences in the source and destination schemas. And for this demo purpose, we will have:

  • Two dates from the source schema that comes in the “yyyyMMdd” format and which need to be transformed to the “dd/MM/yyyy” format. For that we will use the following C# code inside one 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 "";
}
  • We also have two decimal values from which we need to change the decimal mark (or decimal separator) from a dot (“.”) to a comma (“,”). For that we will use the following C# code inside a Scripting Functoid:
public string ChangeDecimalSeparator(string inputDate)
{
      return inputDate.Replace(",",".");
}

According to my previous post, we will have something like this:

Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Scripting-Functoid

In the first Scripting Functoid, we will have to specify the body function and in the second functoid, only the function declaration (without the body). Actually, it doesn’t matter if it is the first or the second — the point is that in one of them you need to have the body of the function and the others you just need the function declaration.

So, what is “wrong” with this approach? Or better, what are the limitations of this approach?

Well, nothing is wrong with this approach. However, it has some limitations that you need to be aware of:

  • If I need to change something in the code of the function in this sample, where we have a function that is used twice, it is still easy to find the Scripting Functoid that has the function body declared. Even if we have to go one by one, we would only have to do it twice. But let’s imagine that we have several ones… It will probably start to become a problem in terms of maintaining this map, at least we will probably spend unnecessary time to fix small and simple problems. To minimize this issue, we can implement a best practice to always put the code in the first functoid. But still, we are only minimizing this issue/limitation.
  • Now let’s imagine that this is a big map and that contains several grid pages and that we use the same Inline C# Function in at least two different grid pages. You may be thinking that is exactly the same, one of them should have the body and the others only the declaration… but it is not! To prove this let’s see what happens if we try to move the Scripting Function with the body code to a different grid page:

Try-Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Different-Grids-Scripting-Functoid

  • You may end up receiving an error saying “Unable to write output instance to the following <…>. ; expected”. The rule here is:
    • The Scripting Functoid that specifies the body function needs to be on the leftmost grid page and the remaining Scripting Functoids (with the function body declared) on the other grid pages to the right. In other words, counting the grid pages from left to right, if the Scripting Functoid that specifies the body function is on the second grid page, the remaining functoids with the function body declared, cannot be placed on the first grid page, they can only be placed from the second grid page (including the second).

For these limitations, this approach can be, or become, a problem in some scenarios in terms of:

  • Development: from a development perspective, even if this is a very straight forward task, trying to decide where to put the function body code and the declarations can be a complicated task, all of them presenting challenges in terms of a maintenance perspective.
  • Maintainability: From a maintenance perspective, if we need to change something within these inline codes, try to find in which Scripting Functoid the body code is declared can be a time-consuming task.

Knowing all this, the main question is: How can we improve this approach in a way that we can solve all of these limitations?

After considering several approaches, keeping in mind that the purpose here is to maintain the use of Inline C# Scripting Functoids (not changing to approaches that use external Assemblies or Custom Functoids), I thought that the ideal solution would be to create a concept of Global Functions that I could use them anywhere within my map but, having a unique place with quick and easy access where I can maintain the code.

You can download the entire article as a PDF document.
How to create “Global C# function” to be reused inside a map?.

Creating a “Global C# function” to be reused inside a map

To create the concept of Global C# Functions that you can use inside your maps, you need to:

  • Add a Grid page to your map and rename it to “GlobalFunctions”
    • of course, this is just a suggestion/best practice that allows you to easily identify the grid page that contains the common code of your map
  • Set this grid as the first grid page of your map

Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Different-Grids-First-grid-Scripting-Functoid

    • This set is important, it allows you to have quick access to your global functions but it also avoids you having compile error – “Unable to write output instance to the following <…>. ; expected” – because of the same rules identified earlier
  • Drag-and-Drop a Scripting Functoid to the “GlobalFunctions” grid page and place the C# code, taking, for example, the Date conversion:
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 "";
}
    • Do the same for the change decimal mark function
  • Do not link any inputs and don’t map (link) this Scripting Functoids to any element in the destination Schema.

Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Different-Global-Function-Scripting-Functoid

Now you can use these functions in other grid pages using only the function declaration:

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

or

public string ChangeDecimalSeparator(string inputDate)

However, if you try to compile or test the map, you will see that there will be some errors and your map will not work. The same “Unable to write output instance to the following <…>. ; expected” error that we already saw above.

Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Different-Global-Function-compile-error-Scripting-Functoid

To solve this error and to be able to create the concept of Global Functions you need to:

  • Double click the earlier Scripting Functoids added to the “GlobalFunctions” grid page and set the expected input values as empty constant values, that by default doesn’t exist

Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Different-Global-Function-properties-Scripting-Functoid

    • For the FormatDate Function that accepts 3 inputs: string inputDate, string inputFormat, string outputFormat; you should add 3 empty constant values

Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Different-Global-Function-properties-fixed-Scripting-Functoid

    • And for the ChangeDecimalSeparator Function that accepts 1 input: string inputDate; you should add 1 empty constant value

Reuse-Inline-Csharp-Functions-In-BizTalk-Maps-Different-Global-Function-properties-fixed-2-Scripting-Functoid

This should do the trick. Now if you compile or test your map everything will work.

You can find all the source code here:  BizTalk Mapper Tips and Tricks: How to create a Global Inline C# Function

You can download the entire article as a PDF document.
How to create “Global C# function” to be reused inside a map?.