Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

The *Other* Way of Using BizTalk Dynamic Maps

4.33/5 (2 votes)
21 May 2009CPOL5 min read 41.9K   281  
An article on using dynamic BizTalk Maps using XSLTs

Introduction

This article discusses the alternate way of using dynamic Maps in BizTalk Orchestration.

Yes, there has been an article about dynamic BizTalk maps before; this approach is quite different, and this may prove to be a bit more flexible depending on what you are trying to achieve in your BizTalk project.

This article is intended for developers with working knowledge of BizTalk 2004/2006/2009. It is not intended as BizTalk mapping 101.

BizTalk Maps: A Retrospective

If you have been using BizTalk in creating integration solutions, you are probably aware that there has been a drastic change in the implementation of maps in BizTalk from BizTalk 2002 to BizTalk 2004/2006/2009 (BizTalk 2004, 2006, and 2009 are using essentially the same "engine"). In BizTalk 2002, Maps (Microsoft speak for "XSLT") are stored in an IIS server. If you need to change something in your BizTalk map, you just edit the map using the BizTalk Map Editor and save it in the same location on the IIS server, then issue a "Refresh" command, and you have your updated BizTalk map.

In BizTalk 2004/2006/2009, however, this has changed with the transition from COM to .NET, and the designers of BizTalk felt that it would be simpler and more efficient to convert the Maps into binary code (.NET assembly). This removes the dependence on an IIS server, and theoretically it is faster since it is compiled into an assembly.

What has also been introduced in BizTalk 2004/2006/2009 is the ability to reference an external XSLT file to do the transformation itself. This opens up a whole world of options, as you are not restricted to using what is offered on the Map Editor, you can use a more full-featured XSLT IDE, and using a more standard XSLT promotes reuse of code outside of BizTalk.

While an elegant design by itself, this introduced a minor inconvenience: changes in your Map--whether using an external XSLT or not--requires you to re-compile your code every time you make changes to your XSLT. Hotfixes to your maps can be an annoyance, since you need to compile, then GAC the assemblies to each BizTalk server.

This approach essentially brings back the ability to update your maps (that uses external XSLTs) without having to recompile your BizTalk assembly. This combines the best of both worlds: the ability to use standard XSLTs of BizTalk 2004/2006/2009, and the ease of updating them a-la BizTalk 2002!

The Code

I have included a Sample BizTalk Application in an attempt to provide a clearer view of how to use the BizTalk Dynamic Map as opposed to giving a step-by-step process of building a new application using the Dynamic Map.

What's in the Package?

The zip file should contain two projects:

  • A C# Assembly project, BizTalkDynamicMap, that is the heart of the Dynamic BizTalk Map Loader.
  • A BizTalk project, BizTalkDynamicMapTest, which contains a Source Schema, a Destination Schema, and our Dynamic BizTalk map that is housed in a "regular" BizTalk map. Don't worry if you're confused at this point; hopefully, it will make sense later on.

Let's Use It!

Here are the steps in running our "Hello, World!" Dynamic BizTalk Map.

  • Build the solution. The solution compiles and signs both projects, and the BizTalkDynamicMap DLL is installed in your GAC
  • Copy the files in Artifacts folder from the BizTalkDynamicMapTest project and copy it to C:\DynamicMaps in your local machine.
  • Assign 01.xml in the Sample Files directory in the BizTalkDynamicMapTest project as input to DynamicMapTest.btm.

    Assigning the sample XML input file

  • Right-click on DynamicMapTest.btm and select "Test Map". You should see the output of your map looking like this:
    XML
    <?xml version="1.0" encoding="utf-8"?>
    <dst:Destination xmlns:dst=
      "http://BizTalkDynamicMapTest.Schemas.TestDestinationSchema">
      <!--1.0.0.0-->
      <Child ChildAttr="This is from the source XML's
              //Child/@ChildAttr: Hello, World!" />
    </dst:Destination>
    

What Just Happened There?

DynamicMapTest.btm references two files, an external XSL DynamicMap.xsl and DynamicMapExtensionObjects.xml.

DynamicMapTest.btm referenced files

Opening the DynamicMapExtensionObjects.xml reveals the C# Assembly class BizTalkDynamicMap reference so we can use it in DynamicMap.xsl.

XML
<?xml version="1.0" encoding="utf-8"?>
<ExtensionObjects>
  <ExtensionObject 
     Namespace="http://codeproject.com/BizTalk/DynamicMap" 
     AssemblyName="BizTalkDynamicMap, Version=1.0.0.0, 
		Culture=neutral, PublicKeyToken=cc6595295b2596e4" 
     ClassName="BizTalkDynamicMap.BizTalkDynamicMap"/>
</ExtensionObjects>

When you open DynamicMap.xsl, you see this section of the code:

XML
<xsl:template match="/">  
  <xsl:variable name="xml-doc" select="userJScript:GetXMLDoc(.)" />
  <xsl:variable name="dynamic-map-result" 
	xmlns:DynamicMap="http://codeproject.com/BizTalk/DynamicMap" 
    select="DynamicMap:Transform('default', $xml-doc)"  />
  <xsl:value-of select="$dynamic-map-result" disable-output-escaping="yes" />  
</xsl:template>

xml-doc contains the input XML, dynamic-map-result calls the Dynamic XSLT loader (and executes the XSLT), then dynamic-map-result contains the result of the XSLT.

The first parameter of DynamicMap:Transform holds the key to what XSLT is dynamically loaded. This section of the Dynamic map loader C# class is called:

C#
public string Transform(string loader_name, object key, object input)
{
  try
  {
    IDynamicMapLoader loader = DynamicMapLoaderFactory.GetDynamicMapLoader(loader_name);
    Trace.Assert(loader != null);
    
    XslCompiledTransform map = loader.LoadDynamicMap(key);
    XmlReader input_doc = XmlReader.Create(new StringReader(input.ToString()));
    StringWriter output_doc = new StringWriter();
    XmlWriterSettings xs = map.OutputSettings.Clone();
    
    map.Transform(input_doc, XmlWriter.Create(output_doc, xs));
    
    return output_doc.ToString();
  }
  catch (Exception e)
  {
    Trace.WriteLine("exception: " + e);
    return "";
  }
}

Wait. How is this "Dynamic?" 

Well, if you look at the BasicDynamicMapLoader code, it loads a lookup XML, maplookups.xml. If you open it, you should see that the default is mapped to "MyDynamicMap.xsl".  Yes, the XSL that produced the "Hello, World" XML. 

Changing the MyDynamicMap.xsl will change the output. 

Hey. I Updated the XSL and the Output is the Same! 

BasicDynamicMapLoader has a primitive caching using a Hashtable.  Since Visual Studio loads the BizTalkDynamicMap DLL when running the DynamicMapTest.btm, the XSL gets "cached".  In order to disable the caching, comment out this code in BasicDynamicMapLoader

C#
ht.Add(key.ToString(), xsl); 

Then rebuild, then restart Visual Studio (to re-load the new DLL upon testing the *.btm file).  Any updates to MyDynamicMap.xsl should be reflected whenever you test the *.btm file.  

What's the Difference from that "Other" Dynamic Map

Since the dynamic map code is housed in a "Regular" BizTalk map, you can use this dynamic map in *any* BizTalk artifact that needs maps: not only in Orchestration, but also in Receive Ports and Send Ports! And, since this is truly dynamic, make a change in the XSLT and it reflects it in the map immediately!

What's Next?

The IDynamicLoader implementation is pretty basic at this point: it loads the XSLT from the filesystem, then caches it to memory using Hashtable. This is perfectly usable at this point, but there are several ways to improve upon it: you can load the XSLT from anywhere, including an HTTP server or a database, and use a more sophisticated caching like Enterprise Library Caching block.

You can also modify the DynamicMap.xsl to use different "keys" depending on the input source schema. In fact, this is what I have done in one of my projects.

History

  • Version 1.0 - 20 May 2009
  • Version 1.1 - 21 May 2009
    • Added notes on the BasicDynamicMapLoader 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)