Yossi Dahan [BizTalk]

Google
 

Monday, September 25, 2006

Taking in and returning Xml from an extension object in xslt

In most cases, when I'm using extension objects from xsl it is with simple type data passed into the component or retrieved from it.

However, in some cases I need to either pass in or retrieve a full xml fragment.

While in many cases it makes sense to get the information before the map execution and then use BizTalk's ability to map multiple documents to one, in some cases it makes perfect sense to get the information directly from the map.

An good example, I believe, is when you need to perform data enrichment - to get more information based on data from the message.

Of course you could have used xpath queries in an expression shape to extract the bits of information you need to work on to variables, then get the data you need through method and web services calls before the map, and use a many-to-one mapping to merge it all, but wouldn't it be nice if you could simply, from within the xsl, call a .net class, pass the value of, say, an attribute, and get back a node set you could iterate on and transform to your format output?

Well - you can.

All you need to do is write a method that returns the type System.Xml.XPath.XPathNodeIterator.
(You could also take in a parameter of this type if you need to receive an xml node-set to your method.

A quick example of such a method is:

public XPathNodeIterator processXmlNodeSet(XPathNodeIterator nodeSet)
{
nodeSet.MoveNext();
XPathNavigator nav = nodeset.Current;
XPathNodeIterator i = nav.Select("//city");
return i;
}



Of course this method doesn't do much, but it takes in a node-set, and returns part of it based on an xpath.
Real life implementation of course would be fuller, probably involving creating the output based on database reads or maybe performing some complex manipulation on the input.

If you wanted to create a new node-set (not through selecting nodes in an existing node set) you would need to create an XPathDocument.

XpathDocuments are created from a stream, so you could easily create one from an xml in a file, or simply write your xml to a stream such as a memory stream.

With an XpathDocument you can use the CreateNavigator method to create an XpathNavigator and then use the navigator's select method to select the xml fragment you need and return the XpathNodeIterator you need

One last node - I have only tried this approach using custom xsl from a map and using xsl in a standard .net application. I have not tried using this approach with through the mapper with functoids (as I rarely work that way)

Friday, September 22, 2006

Wish list: adding xslt files to BizTalk projects

As you probably know by now, I use xslt scripts extensibly in my maps.
This is simple, quick and powerful once you get used to it.

What is not so simple and quick is adding the xslt file to the BizTalk project because Visual Studio will not let you add xslt files to a BizTalk project.
What I used to do is create a new xslt file, drop it in the project's folder and then use "Add existing item" to add it to the project.

Today I finally took the 5 minutes it takes to sort this out and modifed the vsdir file to include xslt files in the BizTalk project's Add New Item dialog.

To achieve this simply go to the "Program Files\Microsoft BizTalk Server 2006\Developer Tools\BizTalkProjectItems\Map Files" and add to the file BTSMaps.vsdir something like the following line - "..\xsltfile.xsl{DA9FB551-C724-11d0-AE1F-00A0C90FFFC3}#1429770#142980#68170#14299"

Then simply create xsltfile.xsl in the folder above and you're set to go.

Oh, and one other thing - is it too much to ask for the Visual Studio guys and the BizTalk guys to exchange a couple of emails and agree on the extension for xslt files?

At the moment Visual studio will create an xslt file with the extension ".xslt" while the BizTalk mapper's open file dialog looks for files with an ".xsl" extension.

Am I really the only one annoyed by this?

Wednesday, September 20, 2006

Following a correlation set in an orchestration

I always thought the "Follow correlation set" property on a send shape does not make much sense, after all it is only a receive shape that is really expected to follow a correlation set initialized earlier in the process.

Today I found what it’s useful for.

I knew setting a context property's value in an orchestration keeps it in a "Not Promoted" state. I also got used to initializing a correlation set when sending messages to the message box when I needed to promote certain properties, which is a bit awkward I guess, but is common knowledge.

However - this time I needed to post messages to the message box through a directly bound send port while promoting a couple of properties to allow the messages to be routed correctly from the message box.

With the send shape being in a loop I could not initialize the correlation set there as you're not allowed to initialize a correlation set more then once.

The answer was very simple, but not obvious (I hope, otherwise it’s just a case of me being thick), you can use the "follow correlation set" on a send shape, which, like the initialize correlation set, will make sure the properties defined in the correlation type gets promoted.

Unlike the "initialize correlation set" this can be done as many times as needed.

I guess this is nothing new or surprising to many, but it was for me, which makes it worth posting.

Monday, September 18, 2006

Messages do not appear in the transform shape wizard

This post highlights a situation I've already discussed in a previous post from July from a different angle. You can read the previous post here.

We have our solution, which is quite big and complex by now.
In that we have a few schmas, maps and processes projects with references between them as needed.

Today I've added a new assembly with a couple of processes, I then proceeded to add a transform shape in one of them.

I've added a references to the maps project and to the common types project where the message types needed for the process were defined.

I've created the messages I needed selecting the types from the referenced assembly.

I then dropped a transform shape and selected the referenced map, as expected I see the in the wizard the types of messages I need to select for for the input and output messages.

However, when I try to select the actual messages to use the combo box is empty!!

Iv'e checked and re-checked that the messages are of the correct type (which uses the same schema as defined in the map) and could not find any problem with the settings.

The problem was, of course, that I did not add a reference to the schemas projects.

So while I had the types declared all right, the process did not hold the schema to support them.

With the maps refering to a schema, and the message to a message type for which no schema exist in the process (as there was no reference set) the designed could not match the two.

Adding the missing reference solved the problem, I could now select my messages.

Thursday, September 14, 2006

Enums in web services and BizTalk

With a standard .net project when you add a web reference, a proxy class is generated for you and is added to your project.

This way you can easily create instances of any classes declared by the web service and use them as you need (mainly in order to pass them to the actual web method, but not necessarily)


With BizTalk, however, things are slightly different. A proxy class is still being generated behind the scenes, but it is not added to your project. Instead the equivalent schemas are being generated and added and of course web port types and multi part message types are being created for you.

Generally this is all right, but there are a few cases where the fact you don't actually have access to the proxy is a bit of a pain.

An example of such a case is when the web service takes in a few parameters (represented by a few parts in the request message) and one of them is an enum.

In a standard .net project you could simply use the enum to select the value you want to pass, in BizTalk, when assigning values to the part, the enum is not available for you as the proxy class is not available.

You have to resort to putting in the value you need which kind of defeats the purpose of using an enum in the first place.

This could be solved if the types added in the proxy will be come available as variables in the orchestrations, ideally non initialized (as in many cases you don't actually need them)

Wednesday, September 13, 2006

Wrapping outgoing message in a soap envelope - update

In this post I've described how to wrap an outgoing message in a soap envelope on the send port.

I've just realised it might not be obvious what "system soap envelope schema" means so here it is:

BTS.soap_envelope_1__1+Envelope, Microsoft.BizTalk.GlobalPropertySchemas, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

It is also worth remembering that using the XmlAssembler you're required to have the schema of the document you're outputting deployed to the server as the assemlber will look it up.

Also - using the XmlAssembler has some performance implications which have to be accepted if you're to use this approach.

Tuesday, September 12, 2006

Returning text only from xpath in an orchestration

This is another one of those things I simply keep forgetting.

Using the built-in xpath function in an expression shape is extremely useful for extracting and updating information in messages.

However, it seems that this function is much less forgiving when it comes to the xpath you use.

One example of this I bumped into (again) today is that if I have an xml like this:

<ns0:MyRoot xmlns:ns0='MyNamespace>
<ns0:MyElement>free text here</ns0:MyElement>
</ns0:MyRoot>


And I try to extract the text inside a node an xpath like this -

/*[local-name()='MyRoot' and namespace-uri()='MyNamespace']/*[local-name()='MyElement' and namespace-uri()='MyNamespace']

Or even

/*[local-name()='MyRoot' and namespace-uri()='MyNamespace']/*[local-name()='MyElement' and namespace-uri()='MyNamespace']/text()

While both work nicely in almost every xsl engine, BizTalk orchestration's xpath resolves both to empty strings. To get the text contents of the element you have to use xslt's string function -

string(
/*[local-name()='MyRoot' and namespace-uri()='MyNamespace']/*[local-name()='MyElement' and namespace-uri()='MyNamespace']/text())

Monday, September 04, 2006

Transform gotcha update

In this post I've described a quirk in the Transform shape UI.

By now Microsoft validated this as an issue with the UI and are looking into it, in the mean time I've we've to figure out how to overcome this without needing to create a new map, which turned out to be quite simple really -

If you open the odx file with the problematic transform shape in an xml (or text) editor and search for the name of the transform shape you should find something along these lines -


<om:Element Type="Transform"
OID="d2b31334-64b6-48f6-9636-594a404a6bd4"
ParentLink="ComplexStatement_Statement"
LowerBound="233.1"
HigherBound="235.1">
<om:Property Name="ClassName" Value="****************************" />
                <om:Property Name="ReportToAnalyst" Value="True" />
<om:Property Name="Name" Value="MyTransform" />
                <om:Property Name="Signal" Value="False" />
                <om:Element Type="MessagePartRef" OID="aa2c6493-6f61-4ef5-984f-e716728b73ba"
ParentLink="Transform_OutputMessagePartRef"
LowerBound="234.56"
HigherBound="234.81">
                <om:Property Name="MessageRef" Value="OutputMessage" />
                        <om:Property Name="PartRef" Value="OutputPart" />
                        <om:Property Name="ReportToAnalyst" Value="True" />
                        <om:Property Name="Name" Value="MessagePartReference_5" />
                        <om:Property Name="Signal" Value="False" />
                </om:Element>
                <om:Element Type="MessagePartRef"
OID="01736f65-cc1b-4bd0-9997-4d66d9574b6b"
ParentLink="Transform_InputMessagePartRef"
LowerBound="234.151"
HigherBound="234.176">
                        <om:Property Name="MessageRef" Value="InboundMessage" />
                        <om:Property Name="PartRef" Value="InboundPart1" />
                        <om:Property Name="ReportToAnalyst" Value="True" />
                       <om:Property Name="Name" Value="MessagePartReference_3" />
                        <om:Property Name="Signal" Value="False" />
                </om:Element>
                <om:Element Type="MessagePartRef"
OID="5a7e355c-a71b-46d5-ae73-cfcaa2163359"
ParentLink="Transform_InputMessagePartRef"
LowerBound="234.195"
HigherBound="234.212">
                        <om:Property Name="MessageRef" Value="InboundMessage" />
                        <om:Property Name="PartRef" Value="InboundPart1" />
                        <om:Property Name="ReportToAnalyst" Value="True" />
                        <om:Property Name="Name" Value="MessagePartReference_6" />
                        <om:Property Name="Signal" Value="False" />
                </om:Element>
   </om:Element>


The key things to look at are:

1. The parentLink attribute (which indicate whether an element describes a map output or input and help identifying which elements needs to be edited)

2. The MessageRef which is the name of the message that holds the part being mapped

3. The PartRef which is the name of the part of that message to use.

In my example the input uses InboundMessage.InboundPart1 twice for the input, as I could not select InboundPart2 in the UI.

So - to overcome the UI problem all you need to do is edit that xml, replacing the part you were "forced" to select in the UI with the one you actually want. So I replaced the second "InboundPart1" with "InboundPart2".

When you will then open the odx file with the orchestration designer again, and open the transform shape UI you will see that the new part you've specified appears in the input list.

The message type in the second column of the UI will still show the wrong message type, and you will still not be able to re-select the correct part, so you are still prevented from making any changes to this transfor shape through the UI, but at least you do not need to create a new map every time something changes, you just need to carefully edit the XML.