Click here to Skip to main content
Email Password   helpLost your password?

Introduction to Functoids

I am sure you have heard about functions, but what about functoids? Functoids or BizTalk functoids are, in a way, small reusable functions that you build just like functions. These are like operations that you need to perform specific tasks on data. BizTalk comes with a good collection of readymade functoids. But you will frequently face situations where you desire a simple functionality. Let us say, you want to validate a credit card number, it will be great if we can build a functoid which can take in a credit card number and credit card type, and return true or false. This will be a very good scenario for writing a functoid of your own.

Scenario

As a learning exercise, I suggest building a functoid which calculates the perimeter of a rectangle for a fencing company. The logic of the functoid implementation is really concise:

2   x (length + breadth)

A Bird's Eye View of the Steps

To create a BizTalk functoid, we need to briefly do the following:

Getting Down to Business ..

I have broken down the activity into a series of logical steps.

Step 1: Creating your functoid project

You need to create a functoid as a class library. So we need to select a class library project to begin. Make sure you give a proper namespace name for it as we will need this to load the functoid later, using Reflection. We will use Custom.Biztalk.MathFunctoid as the namespase in our example:

sample screenshot

Step 2: Signing the DLL with a key

You need to have a strong name for this assembly to get it loaded into the toolbox. So create a strong name and sign it:

C:\Samples\MathFunctoid > sn   -k mathFunctoid.snk

Once you have the strong key generated, insert the line below to the AssemblyInfo.cs:

[assembly: AssemblyKeyFile("..\\..\\mathFunctoid.snk")]

Step 3: Give a unique ID for this assembly

We need to give a unique ID for this assembly. Using GUIDGEN from the Visual Studio prompt, generate a new GUID and add the following to the AssemblyInfo.cs:

[assembly: Guid("5DE500CC-45BC-454b-A23D-24449899042C")]

Step 4: Add the class skeleton

We need to have a class to implement this functionality, so add a class and call it CPerimeter (or any meaningful name of your choice):

screenshot

Once the class is added, add the following lines in the namespace inclusion section at the top of your class file:

using Microsoft.BizTalk.BaseFunctoids; 
using System.Reflection;  
using System.Globalization;  
using System.Resources;

Step 6: Add references to BizTalk base functoids

In the project references, add a reference to Microsoft.BizTalk.BaseFunctoids.dll. This DLL implements all the base classes we need to create a functoid.

Step 7: Add a resource file

In Visual Studio, go to File->Add New Item->Resource File.

screenshot

I named the resource file Mathresource.resx for this example. Now, add the following resource strings and specify their custom descriptions:

Resource ID Value Explanation
IDS_CONVERTINTFUNCTOID_ID 6123 A value greater than 6000
IDS_FUNCTOID_NAME �Perimeter� The functoid description in toolbox
IDS_MATHFUNCTOID_TOOLTIP �Calculates the perimeter of a rectangle� What appears on the tool tip
IDS_MATH_DESCRIPTION �Calculates the perimeter� Description of functoid in VS
IDS_PERIMETERFUNCTOID_EXCEPTION "Perimeter functoid threw an exception" Description of exception to the Biztalk subsystem

Now, create a 16 x 16 bitmap and add that to the resource file, and reference it as IDS_MATH_BITMAP using the Resource Editor.

Step 8: Implement the class

To implement this class, we derive our class from BaseFunctoid. And in the class, we load the resource file, and set the different parameters like functoid name, tool tip text, and parameters for the functoid.

public class CPerimeter : BaseFunctoid
{
    static ResourceManager resmgr = new 
    ResourceManager("Custom.Biztalk.MathFunctoid" + 
      ".MathResource", Assembly.GetExecutingAssembly());

    public CPerimeter():base()
    {
        int functoidID;
        functoidID = System.Convert.ToInt32(
          resmgr.GetString("IDS_CONVERTINTFUNCTOID_ID"));
        this.ID = functoidID; 

        // This has to be a number greater than 6000

        SetupResourceAssembly("Custom.Biztalk.MathFunctoid" + 
            ".MathResource", Assembly.GetExecutingAssembly());

        //Set Resource strings , bitmaps

        SetName("IDS_FUNCTOID_NAME");                  
        SetTooltip("IDS_MATHFUNCTOID_TOOLTIP");
        SetDescription("IDS_MATH_DESCRIPTION");
        SetBitmap("IDS_MATH_BITMAP");

        // Minimum and maximum parameters

        // that the  functoid accepts 


        this.SetMinParams(2);
        this.SetMaxParams(2);

        /// Function name that needs to be

        /// called when this Functoid is invoked.

        /// Put this in GAC                    

        SetExternalFunctionName(GetType().Assembly.FullName, 
                    "Custom.Biztalk.MathFunctoid.CPerimeter", 
                    "CalcPerimeter");

        //Category for this functoid.

        this.Category = FunctoidCategory.Math;

        //Input and output Connection type

        this.OutputConnectionType = 
             ConnectionType.AllExceptRecord ;
        AddInputConnectionType(ConnectionType.AllExceptRecord);
    } 
}

Step 9: Implement the function logic

Now, we implement the functoid logic for the function name specified in the above step, using SetExternalFunctionName. The code below trims the incoming values. This is done because in XML, string data that are numerals could contain white spaces.

public string CalcPerimeter(string RectangleLength, 
                            string RectangleBreadth)
{
    int ilength = 0;
    int ibreadth = 0;
    int iPerimeter = 0;
    ResourceManager resmgr = new ResourceManager("Custom." + 
                             "Biztalk.MathFunctoid.MathResource", 
                             Assembly.GetExecutingAssembly());

    //Remove whitespace

    RectangleLength = RectangleLength.Trim();
    RectangleBreadth = RectangleBreadth.Trim();


    if ( IsNumeric(RectangleLength) && IsNumeric(RectangleBreadth) )
    {
        try
        {
            ilength = Convert.ToInt32(RectangleLength, 
                      System.Globalization.CultureInfo.InvariantCulture);
            ibreadth = Convert.ToInt32(RectangleBreadth, 
                       System.Globalization.CultureInfo.InvariantCulture);
            iPerimeter = 2  * (ilength + ibreadth);
        }
        catch
        {
            throw new Exception(string.Format(resmgr.GetString(
                                "IDS_PERIMETERFUNCTOID_EXCEPTION"), 
                                RectangleLength +  " "  + 
                                RectangleBreadth));
        }
    }                      
    return iPerimeter.ToString() ;
}

Step 10: Compile and Deploy

You are now ready to build and deploy your functoid. Once it is built, copy the Custom.Biztalk.MathFunctoid.dll to Drive:\Program Files\Microsoft BizTalk Server 2004\Developer Tools\Mapper Extensions.

Now, make the DLL available in the GAC, using the following command line operation:

C:\> gacutil /if Copy the Custom.Biztalk.MathFunctoid.dll

Step 11: Adding the functoid to the ToolBox

Open a BizTalk project and go to toolbox, and then right click on the toolbox. Go to Add/Remove items, select the Functoids tab, and browse to Custom.Biztalk.MathFunctoid.dll in the mapper extension folder, and check it.

You should now see your functoid in the toolbox under the list of Mathematical functoids (because we set the category as Math, remember?).

sample image

Step 12: Take a deep breath!

Congrats, you just finished your first custom BizTalk functoid and I am sure it wont be your last!

Points of Interest

Testing the functoid

I have included a small map to test the functoid. You can download this project in the source available for download at the top of this article. It is titled customFunctoid Map.

sample image

Gotcha's

You cannot insert a bitmap directly into the resource editor, you will have to use ResEditor to do it. The ResEditor can be found here: C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Samples\Tutorials\resourcesandlocalization\reseditor.

Exceptions

You might get an exception that the functoid was not found.

Exception Caught: Functoid not found: 
  guid({5DE500CC-45BC-454b-A23D-24449899042C})  funcid(6123)

This happens when you GAC the DLL but forget to copy it to the mapper extension folder.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalhi my friends
eyespi
3:11 1 Mar '10  
our biztalk server gets slow down or ports are not reacting while files are being received cos of the ports amount over 200.I would be grateful if u have any idea for the solution Smile
GeneralBiztalk Custom Functoid Wizard
LeandroDG
5:29 15 Sep '09  
Hello there!

I’ve posted my Biztalk Custom Functoid Wizard in my blog, it’s pretty similar to the Adapter Wizard and Pipeline Component Wizard created by Scott, Boudewijn and Martijn.

I hope everyone can find it of use: http://www.leandrodg.com.ar/blog/2009/06/biztalk-functoid-wizard-for-biztalk-2004-2006/

Cheers,
Leandro.

Leandro Díaz Guerra
Lagash Systems S.A. From Argentina, to the world

GeneralExpecting more deep Article
Member 3515827
6:17 2 May '08  
this is really Very good Articles. Expecting more deep Article from you.

Thanks
GeneralGood one
DEEPCHAND_KOSTA
20:19 28 Jun '07  
it's really good article

DCKOSTA

GeneralGUIDGEN
ThunderBiz
10:12 4 Apr '07  
[assembly: Guid("5DE500CC-45BC-454b-A23D-24449899042C")]

Hi folks,

GUID generates a error. error CS0246: The type or namespace name 'Guid' could not be found (are you missing a using directive or an assembly reference?):(
GeneralRe: GUIDGEN
_ABHILASH_MS_
10:29 7 Jun '07  
Are you using the guidgen tool ?


GeneralScript Functoid in External assembly
Nehal
7:22 9 Feb '07  
Guys -

I am a new comer to the BizTalk but I have been .Net developer for past 4-5 years and been involved in design and architecturing the small to medium size projects. I have been in IT for almost 7-8 years.

We will be using BTS 2006 for our development and I was thinking of coding my scripting functoids in external assembly. The reasons are easy maintainence, can use advances oops concepts, do not need to redeploy map everytime you change a line in your functoid code..etc,

However, one of my coworker raised two concern
1) In Biztalk if you write functoids in external assembly, it would be hard to maintain as you have to keep track of version info, deploy the external assembly in each of the testing and production environment.
Is there a way in BT map project to deploy all referenced assemblies in GAC along with map? Or we have to do it seperately?
2) Performance of Biztalk will degrade as it has to load external assembly in memory and execute functions from there? This argument does not sounds solid. What degree the performance can degrade ? 1-2% 20-30%?

I would appriciate your help. If you can also provide some links or documentation that answers all this question, wil be really cool.


Thank you much




Nehal Jain

GeneralRe: Script Functoid in External assembly
_ABHILASH_MS_
10:28 7 Jun '07  
You dont need to redeploy the map every time you change a line.. You should be able to just gac the changes and restart the service to take effect . So in short that should not be a reason to write functions in external assembly. Build a functoid if you think it will get called a lot of time and is generic enough liek for eg a Creditcard number validator but if you have a specific function you can use external assembly


GeneralSweet !!!
krazykoder
6:42 28 May '06  
I was trying to solve an issue where I didnt want to hard code my db connection string in a map where i was using the database lookup functoid. I used this template to create a custom functoid that reads my connection string (or any other config key) from an external config file. My Functoid takes 3 params, ConfigFilePath, ConfigFileSection, & ConfigFileKey. I also using VS 2005, .Net 2.0, & BizTlak 2006. Now i can use my functoids return value to pass in the db connection string to DB Lookup functoid. As long as I have my config file in the same location on Dev, Test, & Production boxes I don't have to worry about changing code in the map when i deploy my BizTalk solution to different environments. Other solutions i was reading about were using SSO, or the Rules Engine to store db connection string, but i still wasnt sure if I could use those solutions from a map as opposed to an orchestration. This seemed easier to accomplish than the other methods & I can reuse this functoid in other BizTalk projects Smile

Great Sample!
GeneralRe: Sweet !!!
_ABHILASH_MS_
8:01 29 May '06  
Loks a very cool Functoid.. Cool

www.abhilash.in www.biztalkcafe.com http://biztalkland.blogspot.com
GeneralI tried this - it works like a dream....
vish1979
7:22 13 Feb '06  
Hey, nice article.

Just a quick note, if your a beginner to Biztalk/C# have a look at the source code avaiable and make sure you set the bitmap/icon using te resource editor available with the .NET SDK or use this:

Resourcer for .NET @ http://www.aisto.com/roeder/dotnet/[^]

if you use one of the two you should be fine.

vish1979
GeneralRe: I tried this - it works like a dream....
_ABHILASH_MS_
22:12 24 Mar '06  
Glad to know it helped for you .

www.abhilash.in www.biztalkcafe.com http://biztalkland.blogspot.com
GeneralNice one Abhilash
Owner drawn
21:15 26 Jan '06  
Cool articleCool

Jesus LovesRose
<marquee direction="up" height="40" scrolldelay="1" step="1" scrollamount="1" style="background:#aabbcc;border-bottom:thin solid 1px #6699cc">
--Owner DrawnRose
--Nothing special
--Defeat is temporary but surrender is permanent
--Never say quits
--Jesus is LordRose
</marquee>

GeneralRe: Nice one Abhilash
_ABHILASH_MS_
9:16 27 Jan '06  
Thank you for your compliments

www.abhilash.in www.biztalkcafe.com http://biztalkland.blogspot.com
GeneralNice article
malharone
6:28 26 Jan '06  
I'll post a code on creating AICs for BTS 02, and you can update your article. I'm currently away from my primary dev machine, but will post it tomm.

- Malhar
GeneralRe: Nice article
_ABHILASH_MS_
7:41 26 Jan '06  
Looks like I have started a BizTalk Cult ! , Hope we wil have a nice juicy BizTalk or an "Enterprise Servers" section soon . I have another one in the works Blush

www.abhilash.in http://www.biztalkcafe.com

http://biztalkland.blogspot.com/



-- modified at 12:41 Thursday 26th January, 2006
GeneralRe: Nice article
malharone
14:17 2 Feb '06  
Here's the C# template code to create custom AICs using BTS APIs.

- Malhar

using System;
using System.Text;
using System.Threading;
using System.IO;
using System.EnterpriseServices;
using System.Runtime.InteropServices;
using Microsoft.CommerceServer.Interop;
using Microsoft.CommerceServer.Runtime;
using PipeCompLib;

namespace MyNamespace
{
[Guid("{08229782-89C8-4028-BB74-75BB58EF1400}")] //<-- this is your custom guid
[ClassInterface(ClassInterfaceType.AutoDual)]
public class MyBizTalkAIC:ServicedComponent, IPipelineComponent, IPipelineComponentAdmin
{
public MyBizTalkAIC()
{
}

void IPipelineComponentAdmin.SetConfigData( object oDictionary )
{
}

object IPipelineComponentAdmin.GetConfigData()
{
return null;
}

public static void ProcessTransaction (string transaction)
{
//my custom code
}

[AutoComplete]
int IPipelineComponent.Execute( object oDictionary, object oDispContext, int iFlags )
{
string data="";

Microsoft.CommerceServer.Runtime.IDictionary dict= (Microsoft.CommerceServer.Runtime.IDictionary)oDictionary;
data = (string) dict["working_data"];
data = GetNormalizedString(data);

ProcessTransaction (data);

return 0;
}

private string GetNormalizedString(string strInputMessage)
{
string strNormalized="";
try
{
byte[] bMessage = System.Text.Encoding.Unicode.GetBytes( strInputMessage );

// The best way to start at a known starting char is to find the 1st '<'
int startPos = 0;
int EndPos = bMessage.Length;
while (startPos < EndPos)
{

if (bMessage[startPos] == (byte)'<')
break;
startPos++;
}
if (startPos == EndPos) // It is bad if we don't find the beginning byte of the XML
startPos = 0; // WHAT TO DO ?? this is safest - BUT it could be < !!!

// This is actually wrong when Unicode because we find the '>' but it should be with its second byte
// BUT - it does not hurt later when converted
while (EndPos > startPos)
{


if (bMessage[EndPos -1] == (byte)'>')
break;
EndPos--;
}
if (EndPos == startPos) // It is bad if we don't find the End byte of the XML
EndPos = bMessage.Length; // WHAT TO DO ?? this is safest - BUT it could be > !!!
startPos=0;
EndPos=bMessage.Length-1;

// Without doing UnSafe code (pointer games), the following just makes a new copy of the byte[] starting at the '<'
// AND ending at the last '>'
// - in affect: making the trimmed string in byte format
byte[] bNewStartMsg = (byte[])Array.CreateInstance(bMessage.GetType().GetElementType(), EndPos - startPos);
System.Array.Copy( bMessage, startPos, bNewStartMsg, 0, (EndPos - startPos));



if ( bNewStartMsg.Length >= 6 ) // Array access ( [] ) will fail if out of range
{
if( (bNewStartMsg[1] == 0) && (bNewStartMsg[2] == 0) && (bNewStartMsg[3] == 0) ) // msg is DOUBLE UNICODE !!!
{
string tStr = System.Text.Encoding.Unicode.GetString(bNewStartMsg);
Encoding encodingUTF8 = System.Text.Encoding.UTF8; // Thanks to Richter .Net p291
byte[] tArray = encodingUTF8.GetBytes(tStr); // This does the 2 byte-unicode to UTF8 conversion
strNormalized = System.Text.Encoding.Unicode.GetString(tArray); // Hope this is basically a cast
}
else if( (bNewStartMsg[3] != 0) && (bNewStartMsg[5] != 0) ) // msg is ASCII
{
strNormalized = System.Text.Encoding.ASCII.GetString( bNewStartMsg ); // This is a Ascii to Unicode conversion
}
else {
if ( (bNewStartMsg.Length % 2) == 1 )
{ // IT IS: odd Byted so ReCopy the signal with the extra byte
EndPos++;
byte[] bTempMsg = (byte[])Array.CreateInstance(bMessage.GetType().GetElementType(), EndPos - startPos);
System.Array.Copy( bMessage, startPos, bTempMsg, 0, (EndPos - startPos));
strNormalized = System.Text.Encoding.Unicode.GetString(bTempMsg); // This is vertually a cast of the trimmed byte[]
}
else
{


strNormalized = System.Text.Encoding.Unicode.GetString(bNewStartMsg); // This is vertually a cast of the trimmed byte[]
}
}
}
else {
strNormalized = System.Text.Encoding.Unicode.GetString(bNewStartMsg);
}
}
catch {
}

//trace(strNormalized);
return strNormalized;
//strNormalized = strNormalized + "\n"; //Lets add a NewLine to make sure the XML is read correctly
}


void IPipelineComponent.EnableDesign( int bDesign ) { }

[ComRegisterFunction]
static public void RegisterFunction( Type oType )
{
Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(
"CLSID\\{" +
oType.GUID.ToString().ToUpper() +
"}\\Implemented Categories\\{5C6C30E7-C66D-40E3-889D-08C5C3099E52}" );
Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(
"CLSID\\{" +
oType.GUID.ToString().ToUpper() +
"}\\Implemented Categories\\{BD193E1D-D7DC-4B7C-B9D2-92AE0344C836}" );
}

[ComUnregisterFunction]
static public void UnregisterFunction( Type oType )
{
Microsoft.Win32.Registry.ClassesRoot.DeleteSubKey(
"CLSID\\{" +
oType.GUID.ToString().ToUpper() +
"}\\Implemented Categories\\{5C6C30E7-C66D-40E3-889D-08C5C3099E52}" );
Microsoft.Win32.Registry.ClassesRoot.DeleteSubKey(
"CLSID\\{" +
oType.GUID.ToString().ToUpper() +
"}\\Implemented Categories\\{BD193E1D-D7DC-4B7C-B9D2-92AE0344C836}" );
}
}
}

GeneralHey !
Mauricio Ritter
6:08 26 Jan '06  
Hey ! Greate Article !

We should have a Biztalk Section here at Code Project... or at least a "enterprise servers" generic section.



Mauricio Ritter - Brazil
MSN: mauricioritter(atsign)hotmail.com
English is not my native language so, if you find any spelling erros in my posts, please let me know.
GeneralRe: Hey !
Nishant Sivakumar
6:36 26 Jan '06  
Mauricio Ritter wrote:
We should have a Biztalk Section here at Code Project... or at least a "enterprise servers" generic section.

Once there are enough articles, maybe Smile

Regards,
Nish


GeneralRe: Hey !
Mauricio Ritter
7:21 26 Jan '06  
I've some article written in portuguse. I'll translate them and post them here.

Mauricio Ritter - Brazil
MSN: mauricioritter(atsign)hotmail.com
English is not my native language so, if you find any spelling erros in my posts, please let me know.
GeneralRe: Hey !
_ABHILASH_MS_
7:39 26 Jan '06  
Wil be great to see more Biztalk Authors on CP Big Grin

www.abhilash.in www.biztalkcafe.com http://biztalkland.blogspot.com/
GeneralExcellent article
Bernhard Hofmann
22:51 25 Jan '06  
Well done. This is an excellent article, simple to follow, well written, and a very interesting topic. More BizTalk articles please!

You have my 5.

Don't worry, nobody lives forever.
GeneralRe: Excellent article
_ABHILASH_MS_
4:45 26 Jan '06  
Thanks Bernhard Big Grin , I am now inspired to write more !

www.abhilash.in http://www.biztalkcafe.com

http://biztalkland.blogspot.com/


GeneralGood work!
Nishant Sivakumar
7:40 25 Jan '06  
How about an intro article on Biztalk for those of us who aren't very familiar with it?

Regards,
Nish


GeneralRe: Good work!
_ABHILASH_MS_
9:11 25 Jan '06  
Thanks for the compliment Nish ,I will definitely write maybe a series of articles on BizTalk Server



http://www.abhilash.in
http://www.biztalkcafe.com
http://biztalkland.blogspot.com/


Last Updated 25 Jan 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010