Wednesday, August 8, 2007

Easing the pain of BizTalk Mapper

I hate the BizTalk Mapper.

I love BizTalk. I think it's an intriguing platform. Developing for it is satisfying - it's fun to see everything come together and work. I have enjoyed simultaneously learning about BizTalk and learning more about the paradigm of XML structures and how to manipulate them with XSLT the .NET framework. For the most part, the Biztalk tools work great. It takes some practice but after a while one can become very good at identifying issues and fixing them quickly.

The Mapper brings the whole BizTalk Server platform down a few notches. It's terrible. Essentially what Microsoft has tried to do is implement a happy GUI over XSLT design, and include some nifty little reusable code snippets at the same time. Unfortunately, that happy GUI is horribly non-transparent (and I don't mean Aero transparent) and hard-to-use, and it generates crap XSLT that is just plain wrong. The worst part is that you can't even clearly see what it's doing unless you make a few backflips through flaming hoops and essentially revert to using XSLT 1.0, which is what the Mapper is based on and what you should be using in the first plcae.

Let me back off of that a bit - the interface is on the right track, it just needs to evolve about four generations before its usable for enterprise-size projects, and it needs to provide a real-time view of what is happening with the XSLT underneath and let you fix what it occasionally, but so horribly, screws up. I envision something like an HTML editor that gives you WYSIWYG and code at the same time. If you poke around on the Internet, you'll notice that most examples you'll see of the Mapper in action are on bite-sized schemas, illustrating the use of one particular functoid. There's two reasons for that:
  1. If the sample schema was any larger you wouldn't be able to figure out what on Earth was going on. I've got enterprise maps that look like a tangled mess of Christmas lights, complete with colored functoids glowing brightly, saying "Just TRY to figure out what I'm linked to!"
  2. If the map was any more complex, the resulting XSLT would be fundamentally broken in at least one place.
My advice to you is to learn XSLT if you don't know it. The Mapper doesn't fully abstract it for you - odds are you'll have to dive into it to see what the map has goofed up.

/end rant

With that out of the way, I'd like to discuss just how to go about diving into the Mapper and discover more about what's going on.

As I stated above, the Mapper is based on XSLT 1.0 (I point out 1.0 for a reason: 2.0 has lots of useful functions, but they are not available to you. You might be able to change this but I haven't confirmed it yet). As in, when you compile a BizTalk map, what it spits out is an XSLT that accomplishes what you see on the map grid. It has some neat Microsoft-added trimmings as well - all of those functoids are written in C# and are represented as script in the XSLT. When the transform is running inside of the Microsoft engine, it can call into the C# and run it. Writing code in C# can be much easier than writing XSLT templates if you're not familiar with XSLT (and often even if you are), so I'll discuss how to take advantage of that as well.

Unfortunately, what you see on the map grid isn't always so obvious. Just exactly how does that stupid looping functoid work anyway? Why on Earth isn't that logical functoid not returning the value you think it should be? The map has no notion of a debugger, it's fire-and-forget - in this case, forget about trying to figure out what it's doing. There is a better way!

Once you've got a map wrapped up, right-click it in the Solution Explorer and click Validate Map. This will essentially compile the map and ensure that it has no errors. When it's finished, there will be a couple of lines near the bottom of the Output window - one has a link to the "extension object" XML (ignore this) and the other is a link to the output XSLT. Copy the XSLT link (don't open it, as it will open it in Internet Explorer view, i.e. "useless view") and do a File > Open on it to open it in an XSLT editor. The XSLT that will appear is your map.

Now for the great part - if you didn't already know, Visual Studio 2005 includes a real-time XSLT debugger. It's one of the greatest XSLT tools I have ever used, and just like .NET code, it can be very educational to plod along with the execution and see what's not working as expected. In fact, it's even more useful than it is for .NET code - even though the Intellisense for XSLT works great, it won't tip you off to certain kinds of typos, and watching carefully for nonsensical values will help you find those kinds of errors. To run the XSLT debugger, mark your breakpoints just as in a C#/VB.NET file, provide an XML document as input with the Input field in the Properties pane, and select "Debug XSLT" in the XML menu.

There you have it - real-time map debugging. But what else can we do with this knowledge? For starters, if you are having difficulty expressing something in a map and you'd rather just do it in XSLT (especially if you see that the map is just soooo close for one particular piece, but if you could just change that one line...), no problem. Drop a script functoid in your map and set the script type to Inline XSLT. Stick whatever XSLT you want in there and it will get dropped into the resulting map at the insertion point corresponding with the target node you link it to. If you do this, I recommend compiling the map first, then writing your XSLT right there in the map's XSLT output - this way you get the advantage of Intellisense and a sane editor control, you can see just what it'll look like once you drop it in, and you can test it right then and there by doing a Debug XSLT. Once your done, copy your code out and drop it into a script functoid. Is the logic of the whole map screwed up? Click on the map grid and use the Custom XSL Path field in the Properties pane to replace the map altogether with some custom XSLT.

What else can you do with this? I mentioned earlier that the functoids are actually C# code represented as script inside the XSLT. This is actually pretty cool, as there are some things that are expressed much more easily and cleanly with C# than with XSLT. Create a map with some functoids and take a look at its XSLT. Down at the bottom, you'll see this:

<msxsl:script language="C#" prefix="userCSharp">

Everything within this tag is C# enclosed in a CDATA tag. More precisely, it is C# script; the code is just a collection of methods not enclosed in a class, although you can create instances of classes within any code you put there (only a handful of common namespaces have been imported - to use any that aren't you'll have to explicitly reference them, as you can't use using statements here). If you want to write your own C# (or JScript.NET or VB.NET - script tags exist for those languages and you can have more than one language within the XSLT) code to stick in here, there are a couple of ways to do it:
  • If you are replacing the whole map with your own XSLT, just plop it in there, exactly how you see it done in the map.
  • Otherwise, you'll need to introduce it via a script functoid. Create a new script functoid with a .NET language type and put your method(s) in it. All methods within a given .NET language must have unique names - even if two methods with the same name have different signatures, only one will make it into the XSLT. If you happen to use the same name and parameter list as a method that the map compiler uses to achieve the functionality of a functoid, the build operation will fail with an error stating that the method is already used. Note that a script functoid doesn't necessarily have to have inputs or outputs - it can just sit there and contribute code to your XSLT.
Once your code is in there, how do you call it? Again, take a look at a map's output XSLT to see. The following expression, used in any XSLT attribute that accepts expressions, calls the MyFunction method with two arguments, the empty string and the text value for the //Record/Field1 node:

userCSharp:MyFunction(&quot;&quot; , string(Record/Field1/text()))

Very, very nice! Plus, when you are debugging your XSLT, the debugger will step through this code as well!

As you can see, not all is bad about BizTalk's mapping system... as long as you stay away from the Mapping toolitself for all but the simplest transforms. Some might argue that XSLT is harder to maintain, but I personally think the maps are tougher. One small change in the map can set off a cascade of changes in the compiled transform, some of which are impossible to figure out without looking at the XSLT itself, so you might as well work in XSLT to begin with. Sometimes a map can be a great way to get started with an XSLT. I suggest playing around with the Mapper, making some simple connections using out-of-the box functoids, and see how they behave in XSLT. Try fiddling around with your schema as well (change the Max Occurs of some of your nodes and marvel at your map that no longer works) to see how the map compiler reacts.

No comments: