Developing a Custom Cumulative Functoid

Use a custom cumulative functoid to perform accumulation operations for values that occur multiple times within an instance message.

You must implement three functions when developing a cumulative functoid. The three functions correspond to the initialize, cumulate, and get actions that the map needs to perform the accumulation. Before discussing these functions it is important to discuss thread safety.

Writing a Thread-Safe Functoid

The functoid code must be thread-safe because under stress conditions multiple instances of a map may be running concurrently. Some of the points to remember include:

  • Static state must be thread-safe.

  • Instance state does not always need to be thread-safe.

  • Design with consideration for running under high-stress conditions. Avoid taking locks whenever possible.

  • Avoid the need for synchronization if possible.

    BizTalk Server provides a simple mechanism to reduce the complexity of writing a thread-safe cumulative functoid. All three functions have the same first parameter, an integer index value. BizTalk Server assigns a unique number to the index value when it calls your initialization function. You can use this value as an index into an array that holds accumulating values, as shown in the following code:

private HashTable cumulativeArray = new HashTable();  
…  
// Initialization function  
public string InitCumulativeMultiply(int index)  
{  
    cumulativeArray[index] = 1.0;  
    return string.Empty;  
}  

This example uses a HashTable instead of an ArrayList. This is because the initialization function might not be called with in-order index values.

Implementing the Three Cumulative Functions

You will need to implement three functions for each custom cumulative functoid you develop. The functions and the methods you must call in the constructor to set them are summarized in the following table. All of the functions return string values.

Note

You determine the best name for each function, but each function must have the number and type of arguments specified.

Function Purpose Arguments To set a reference To set inline script
Initialization int index SetExternalFunctionName SetScriptBuffer with functionNumber = 0
Cumulation int index, string val, string scope SetExternalFunctionName2 SetScriptBuffer with functionNumber = 1
Get int index SetExternalFunctionName3 SetScriptBuffer with functionNumber = 2

Initialization

Initialization enables you to prepare the mechanisms you will use to perform accumulation. You can initialize an array, reset one or more values, or load other resources as needed. The string return value is not used.

Cumulation

This is where you perform the accumulation operation appropriate for your functoid. BizTalk Server passes in the following three parameters:

  • Index. An integer value representing a map instance. There may be multiple map instances running concurrently.

  • Val. A string containing the value that should be accumulated. Unless you are writing a string Cumulate functoid it is a numeric value.

  • Scope. A string containing a number indicating which element or attribute value should be accumulated. Actual values are determined by an implementation.

    You decide which values to accumulate and which values to ignore. For example, you may ignore values that are not below 0 but throw an exception when a value is not numeric. BaseFunctoid supplies two functions—IsDate and IsNumeric—to assist with validation.

Note

If you use IsDate or IsNumeric in an inline script, be sure to set RequiredGlobalHelperFunctions so that the functions are made available to your script.

The string return value is not used.

Get

When BizTalk Server finishes iterating through all of the values determined by the functoid settings in the map, it requests the accumulated value. The get function has one argument, Index, which is an integer value representing a map instance. Your function should use the index value to find and return the accumulated value as a string.

Example

The following example illustrates how to create a custom functoid for performing cumulative multiplication. It relies on a resource file containing three string resources and a 16x16-pixel bitmap resource.

using System;  
using Microsoft.BizTalk.BaseFunctoids;  
using System.Reflection;  
using System.Text;  
using System.Collections;  
using System.Globalization;  
  
namespace Microsoft.Samples.BizTalk.CustomFunctoid  
{  
    public class CumulativeMultiplyFunctoid : BaseFunctoid  
    {  
        private ArrayList myCumulativeArray = new ArrayList();  
  
        public CumulativeMultiplyFunctoid() : base()  
        {  
            //ID for this functoid  
            ID = 6001;  
  
            // Resource assembly must be ProjectName.ResourceName if building with VS.Net  
            SetupResourceAssembly("Microsoft.Samples.BizTalk.CustomFunctoid.CustomFunctoidResources", Assembly.GetExecutingAssembly());  
  
            // Pass the resource ID names for functoid name, tooltip  
            // description and the 16x16 bitmap for the Map palette  
            SetName("IDS_CUMULATIVEMULTIPLYFUNCTOID_NAME");  
            SetTooltip("IDS_CUMULATIVEMULTIPLYFUNCTOID_TOOLTIP");  
            SetDescription("IDS_CUMULATIVEMULTIPLYFUNCTOID_DESCRIPTION");  
            SetBitmap("IDB_CUMULATIVEMULTIPLYFUNCTOID_BITMAP");  
  
            // Put this string handling function under the Cumulative  
            // Functoid tab in the Visual Studio toolbox for functoids  
            Category = FunctoidCategory.Cumulative;  
  
            // 2 required parameters, no optional parameters  
            SetMinParams(1);  
            SetMaxParams(2);  
  
            // Functoid accepts three inputs  
            AddInputConnectionType(ConnectionType.AllExceptRecord);  
            AddInputConnectionType((~ConnectionType.FunctoidCount) & (~ConnectionType.FunctoidIndex) & (~ConnectionType.FunctoidIteration) & (~ConnectionType.FunctoidCumulative) & (~ConnectionType.FunctoidLooping) & (~ConnectionType.Record));  
            AddInputConnectionType(ConnectionType.AllExceptRecord);  
  
            // Set the output connection type  
            OutputConnectionType = ConnectionType.AllExceptRecord;  
  
            // Set the Initialize, Cumulative and Get functions  
            SetExternalFunctionName(GetType().Assembly.FullName, "Microsoft.Samples.BizTalk.CustomFunctoid.CumulativeMultiplyFunctoid", "InitCumulativeMultiply");  
            SetExternalFunctionName2("AddToCumulativeMultiply");  
            SetExternalFunctionName3("GetCumulativeMultiply");  
        }  
  
        // Initialization function  
        public string InitCumulativeMultiply(int index)  
        {  
            if (index >= 0)  
            {  
                if (index >= myCumulativeArray.Count)  
                {  
                    myCumulativeArray.Add(1.0);  
                }  
                else  
                {  
                    myCumulativeArray[index] = 1.0;  
                }  
            }  
  
            return "";  
        }  
  
        // Cumulative function  
        public string AddToCumulativeMultiply(int index, string val, string reserved)  
        {  
            if (index < 0 || index >= myCumulativeArray.Count)  
            {  
                return "";  
            }  
  
            if (IsNumeric(val))  
            {  
                double dval = Convert.ToDouble(val, CultureInfo.InvariantCulture);  
                myCumulativeArray[index] = (double)(myCumulativeArray[index]) * dval;  
            }  
            return myCumulativeArray[index].ToString();  
        }  
  
        // Get Function  
        public string GetCumulativeMultiply(int index)  
        {  
            if (index < 0 || index >= myCumulativeArray.Count)  
            {  
                return "";  
            }  
  
            return myCumulativeArray[index].ToString();  
        }  
    }  

See Also

Using BaseFunctoid
Developing a Custom Inline Functoid
Custom Functoid (BizTalk Server Sample)