Format of BizTalk 2004 assemblies: Maps
Today, we dive deeply into the undocumented and unsupported bowels of the BizTalk artifacts compiler and examine how maps get compiled to .NET assemblies. The BizTalk 2004 Mapper allows to graphically build a transformation between an XML instance of an input schema to another XML instance conforming to an output schema. Today, we explore some undocumented (and therefore not supported) internal parts of BizTalk 2004.
The BizTalk mapper allows complex transformations between fields. On the map below, Field1 in the destination instance is set to Field1 + Field2 from the input schema:
We already know that when a BizTalk project is compiled, Maps are compiled to a public sealed class type into a .NET assembly. The class inherits from Microsoft.XLANGs.BaseTypes.TransformBase (contained in Microsoft.XLANGs.BaseTypes.dll, available in <BTS Installation Directory>). This class is not supported as explained by this page (look at the bottom of the page).
It is time to dive a little deeper into the generated class. Compiling the ExtendingMapper BizTalk 2004 SDK sample and loading it into the excellent Lutz Roeder's .NET Reflector shows the following:
Let's concentrate on one map: CBRSample.CBRInput2CANMap. The class has a default constructor and few properties, mostly strings or array of strings. There are also a few private constant members (the blue entries with a little lock). When BizTalk compiles a map, it essentially creates and compiles a class close to the following C# class:
namespace <projectName> {
public sealed class <nameOfMapFile> : Microsoft.XLANGs.BaseTypes.TransformBase {
public <nameOfMapFile> () { }
public override string[] SourceSchemas {
get { return new string[1] { " <nameOfInputSchema> " }; }
}
public override string[] TargetSchemas {
get { return new string[1] { " <nameOfTargetSchema> " }; }
}
public override string XmlContent {
get { return " <XsltContent> "; }
}
public override string[] XsltArgumentListContent {
// XsltArgument content. If no argument, returns "<ExtensionObjects />\n"
get { return " <XsltArgumentContent> "; }
}
// --------------------------------
// Private const properties not shown go here
// --------------------------------
}
}
To simplify, I have omitted the private constant members of the class. All the bold underlined words surrounded by angle brackets are placeholders that have to be replaced by an actual string adequate for the map being compiled. Let look at the properties a little more closely:
- SourceSchemas: The source schema(s) names, as strings.
- TargetSchemas: The target schema(s) names, as strings.
- XmlContent: The actual Xslt used to perform the transformation.
- XsltArgumentListContent: Xstl Argument(s) list, if any. These are the extension objects needed to run maps with complex transformations features like "Functoids" (small functions that can be used to extend the capabilities of the mapper). They are essentially implemented as extension objects in Xslt.
The base class Microsoft.XLANGs.BaseTypes.TransformBase is abstract and contains all the properties outlined above plus a few more interesting ones:
- public XslTransform Transform { get { /* Implementation omitted */ }; }
- public XsltArgumentList TransformArgs { get { /* Implementation omitted */ }; }
These members are used to get a System.Xml.Xsl.XslTansform and/or a System.Xml.Xsl.XsltArgumentList suitable to execute the map. Armed with this information, one could build a library that executes BizTalk 2004 maps outside of the BizTalk messaging engine. This is a frequently asked question in microsoft.public.biztalk.* newsgroups.
There are also a few things that may not be immediately obvious if you are looking at the output in Reflector. If the input schema or the output schema is not contained in the same assembly as the map, the compiler will emit a dummy member variable. This variable will have the type of the foreign schema(s). This forces a reference to the schema(s) assembly(ies) to be added to the manifest of the map's assembly. There are also two SchemaReferenceAttribute added to the class to facilitate discovering the input and output schema without creating an instance of the map object.
Remember that this post contains mostly undocumented, unsupported information. This content is provided "AS IS" and for entertainment purpose only. Further versions of BizTalk may change the ways things get compiled and relying on low level details like the ones outlined before will cause your application to break.
Comments
- Anonymous
February 15, 2007
PingBack from http://blogs.rbaconsulting.com/banderson/PermaLink,guid,f388ded1-1fe9-44fe-9982-3d9991ceeb1f.aspx