BizTalk Mapper Patterns: Calling an external assembly from Custom XSLT in BizTalk Server 2010

Posted: July 29, 2012 in BizTalk
Tags: , , ,

This topic is not new in my blog, I’ve already talked about this in the past: Calling an external assembly from Custom XSLT – Custom Extension XML (Grid Property) however, I decided to revisit this functionality within BizTalk Server 2010 and you will know why soon.

So is usual in complex maps to have scripting functoid with custom inline XSLT, and sometimes is useful to call custom .Net components directly from XSLT.

Create .Net Component

To illustrate this functionality, I decided to create a Class Library project: MapperExtensionsFunctions with a simple class where it is implemented a method to format the numbers in three decimal places:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace MapperExtensionsFunctions
{
    public class MappingFunctions
    {
        public MappingFunctions()
		{
		}

        public string ToDecimalPoint(string Input)
        {
            CultureInfo ciEN = new CultureInfo("en-US", false);

            double ConvertionDouble = double.Parse(Input, ciEN);
            string Output = string.Format("{0:0.000}", ConvertionDouble);
            return Output;
        }
    }
}
How can we extend BizTalk map to support this functionality?

You can add a custom extension xml file to your solution in order to declare the namespace and use a method from a .Net assembly from XSLT.

The extension file should look something like this:

<ExtensionObjects>
  <ExtensionObject Namespace="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" AssemblyName="MapperExtensionsFunctions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cdbffba4cc751306" ClassName="MapperExtensionsFunctions.MappingFunctions" />
</ExtensionObjects>

Note: “http://schemas.microsoft.com/BizTalk/2003/ScriptNS0” is the “default namespace”, however you can change it (see more here)

In the properties of your BizTalk Mapper, use the Custom Extension XML property to open the Select Custom Extension XML File dialog box, in which you can select the file that contains the custom extension XML for the map (file above).

Custom-Extension-XML

Finally in your Inline XSLT functoid you can use the methods from the assembly by:

<xsl:variable name="result" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" select="ScriptNS0:ToDecimalPoint(MarketPrice/text())" />

Or in this case, inside Inline XSLT Call Template:

<xsl:template name="PriceTemplate">
  <xsl:param name="market" />
  <xsl:param name="owner" />
  <xsl:element name="Price">
    <xsl:variable name="pmarket" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" select="ScriptNS0:ToDecimalPoint($market)" />
    <xsl:variable name="powner" xmlns:ScriptNS0="http://schemas.microsoft.com/BizTalk/2003/ScriptNS0" select="ScriptNS0:ToDecimalPoint($owner)" />
    <PriceMarket>
      <xsl:value-of select="$pmarket" />
    </PriceMarket>
    <PriceOwner>
      <xsl:value-of select="$powner" />
    </PriceOwner>
  </xsl:element>
</xsl:template>
Houston we have a problem!

After Build, deploy and configure my project with success I decided to test my solution, but I keep getting the following error:

The Messaging Engine failed while executing the inbound map for the message coming from source URL:”C:\TEMP\PORTS\IN_CAR\*.xml” with the Message Type “http://BizTalk.CallingExternalAssemblyFromInlineXSLT.CarInfo#CarProperty&#8221;. Details:”Cannot find the script or external object that implements prefix ‘ScriptNS0′.”

At first glance, this error suggests that the assembly in question is not published in the GAC… However even after re-publish, the assembly in the GAC the problem remained!

Don’t panic, believe it or not, you did everything right… BizTalk Server 2010/Visual Studio 2010 have a bug (or issue): Visual Studio does not persist the path of Custom Extension XML property in the .BTM file.

So if someone wishes to use an external assembly in via a Inline XSLT/XSLT Template scripting functoid they cannot specify the external assembly through an extension xml file.

I tried to install the latest cumulative update package for BizTalk (CU5) and Visual Studio service pack but the issue still remain active (you can see more detail and vote to fix it here)

Workaround (unfortunately it’smanual)

You need to manually edit .BTM file to add this node between the elements </ScriptTypePrecedence> and <TreeValues>:

<CustomXSLT XsltPath="" ExtObjXmlPath=".\ExternalAssembly.xml" />

Note: Is not mandatory the CustomXSLT node stand between the elements </ScriptTypePrecedence> and <TreeValues> however this is normal behavior of the compiler

However this is a workaround that can cause many problems, especially maintaining this issue… but stay tuned, because in my next post I will explain how we can solve this problem in a better way.

Calling an external assembly from Custom XSLT in BizTalk Server 2010 Maps (65.9 KB)
Microsoft Code Gallery

About these ads
Comments
  1. […] BizTalk Mapper Patterns: Calling an external assembly from Custom XSLT in BizTalk Server 2010 […]

  2. Jordan says:

    Sandro, I find this method VERY useful. Thanks for posting,I am looking forward to other helpful tips.

  3. Tobi says:

    The issue seems still be present in BTS2013.

  4. RCH says:

    Hi,

    I have a question about the version that should be specified in the custom extension xml file.

    In a context of several assembly versions deployed side by side, I have my extension file referencing the 1.0.0.0 version of a dll, whereas the BizTalk Project containing the map has a dll reference to the 1.0.0.1 version.

    This is causing an “object reference not set to an instance of an object.” error during map execution at runtime.
    I changed the version in the file to 1.0.0.1 and it worked (no new methods or else added in the last version!)

    Does the custom extension points to the project references or directly to the GAC ?

    That’s weird because I don’t have the error according the used environment.

    Thanks!

    • That’s an excellent question… I didn’t try this scenario but for me it makes sense that the custom extension points directly to the file that is deploy in GAC.

      But again I never try this scenario before so I need to test this behavior to have sure.

  5. Meme says:

    You should use double.TryParse and check the boolean result of that before returning a value. double.Parse will throw an exception if the string isn’t a valid double.

  6. John says:

    I have been tasked with extending an existing Biztalk 2010 project. I need to pass a variable from a web request into a map within Biztalk . I have little experience with Biztalk and was wondering if this process of creating an instance of an object when I recieve the request and referencing it in the map is the best way to go.

    • Hi John,
      For me the best way to accomplish this it really depends in the problem it self, but in general the the best way we will accomplished this is using a support schema and pass multiple shemas to the map by using the “Transform Shape” inside the orchestration (setting multiple input messages under “Source Transform”), i.e, you call the web service inside the orchestration and then send to input messages to the map. Check this source sample: http://code.msdn.microsoft.com/BizTalk-Mapper-Patterns-69cb788d

  7. John says:

    Hi Sandro,

    Thank you for your quick and kind response. I shall attempt your suggestion of multiple schemas.
    The data received from the web request will be passed to an SAP database. The database will then send biztalk the response. It was decided that since the RFC in SAP is non-standard we can change this. So, we will pass the variable into the SAP RFC and simply return it without requiring SAP to do anything with it. Admittedly, the data will go to and from SAP, but it is a single value and will be returned in a table. I can then modify the mapping using the variable passed in. Seems much simpler.

    What do you think?

  8. RCH says:

    Hi Sandro,

    Thank you for your response and sorry for my late post.

    So I just did the test and, you’re right, the extension object file directly references the GAC.
    In case of a missing dll version in the gac, a Test map raises the following message : “could not load file or assembly…”

    Other point, with a “standard” BTM map having a scripting functoid with a C# call, the extension object file is dynamically generated and takes the version referenced in the BizTalk project.
    (it can be seen with a validate map)

    But when this file is created by your own, the version has to be increased manually if necessary because the version referenced by the project does not matter.
    In the context of a BizTalk migration (DLL version increased and generated on a new environment), this manual update in each extension file is required.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s