Richard Seroter of Microsoft has published a useful blog posting at http://blogs.msdn.com/richardbpi/archive/2005/10/27/485696.aspx in which he describes one technique for passing messages of any type through a BizTalk orchestration. I would have liked to use comments to supplement what he has written with a couple of observations, but unfortunately his blog site does not appear to allow this. Of course, I couldn't manage to keep to a couple of brief observations, so I run the risk of appearing to have 'hijacked' Richard's contribution. I hope Richard won't hold this against me.
My first point has to do generally with BizTalk's approach to message typing. One of the fundamental differences between message handling in pipelines and orchestrations is that, within pipelines, messages are treated as opaque type-less byte streams, whereas in orchestrations they are treated as strongly-typed objects. This is a rather simplistic statement, and requires some qualification.
The first thing to understand is that types are not really applied to messages. They are applied to the content of message parts. A message can have multiple parts, although most messages have a single part. One part is designated the 'body' part of the message, and the type of this part is often thought of as the type of the overall message. You see this clearly when you define a multipart message in an orchestration. You type each part sepearatly. For 'single-part' orchestration messages, this nuance is kept hidden from the developer, who thinks in terms of the message as a whole, rather than its single body part. Maybe its just me, but I remember I found this inconsistency slightly confusing when I first encountered BizTalk.
Message part content is fundamentally handled as type-less byte streams within pipelines. Content can be associated with type information, but only in an optional and rather loose fashion by using context properties to encode the type metadata. BizTalk defines the BTS.MessageType property for this purpose. For XML messages, this property is generally set by the XmlDisassembler component according to the following pattern:
namespace#docElemName
If the XML document element (aka 'root element') exists in XML's global namespace, then BizTalk uses the element name alone.
Orchestrations, by contrast, treat message parts as strongly typed objects. You set the type when you define a message in the Orchestration View in Visual Studio, and also when you configure messages in Orchestration ports. For XML messages, the type is based on an XSD schema. Behind the scenes, BizTalk auto-generates strongly typed message part classes by extending the abstract XSDPart class. For non-XML messages, BizTalk extends the abstract DotNetPart class. In both cases the generated classes wrap inner streams containing the content. Note that both classes are derived from the abstract XLANGPart class.
This, then leads to my observations on Richard's posting. Firstly, you can pass any messages through orchestrations in a strongly typed fashion. If the message is not an XML message defined using an XSD schema, then create a .NET class to represent the type instead. The inner content stream will be treated by BizTalk as a serialised instance of this class, so you need to design you class for serialisation/de-serialisation. BizTalk uses .NET's XML serialisation by default, but for non-XML content you can use the .NET BinaryFormatter class, or alternatively create a custom formatter. You can find out more about the role of formatters in .NET serialisation from the MSDN library. As well as defining the class a serializable (use the Serializable attribute and, optionally, the ISerializable interface), you use BizTalk's CustomFormatter attribute to decorate your class in order to tell BizTalk which formatter to use. You don’t use this attribute if you want BizTalk to use XML serialisation.
On at least one project, I have used an XML approach by auto-generating my classes using XSD.EXE (there are better alternative tools). I manually amended one of these classes very substantially in order to build in enumeration facilities (I'll keep that story for another time) and other business logic 'smarts'. I used XML serialisation attributes (XmlType and XmlRoot) to specify the namespace and document element names of the XML at the class level, and then typed my orchestration messages using these classes. This is a very powerful approach indeed. Thanks to the XML attributes, the orchestration sets up the correct subscriptions, just as if I had used an XSD schema (there is a bug in BizTalk Server 2004, fixed in Service Pack 1 that prevents the XmlType attribute from being used to define these subscriptions correctly). I can then pass my message to helper code as an XLANGPart object and use the RetrieveAs() method to get the de-serialised instance of my class. I can even assign new content using the LoadFrom() method. Of course, these same methods work with non XML messages as well.
If you create classes to handle non-XML content, you can use the class name as the MessageType specifier for subscription purposes. You could create a pipeline component to set the MessageType property to this value in order to route messages to your orchestration.
So much for the typed approach. Richard's posting describes one of two 'typeless' approaches. In this model, you can pass message part content through orchestrations in an opaque fashion. This can be useful when you don't want to process the message content in any way, and you don't want your subscriptions to be restricted by any consideration of the message type. As Richard's example shows, you still have access to message context within your orchestration. Richard uses the .NET System.Xml.XmlDocument class to define a message with a DotNetPart part. When this same class is used to type a message in an Orchestration 'receive' port, the Orchestration engine suppresses the BTS.MessageType condition within the subscription it generates. Hence messages of any type can be handled through this one port.
The second 'type-less' approach is to use the undocumented 'Any' XSD schema which Microsoft has pre-defined for you. You will find this is always available when you select schema references when creating a message in Orchestration View or setting the message type in an Orchestration port. The effect is much the same as when using System.Xml.XmlDocument. Again, Orchestration receive ports suppress the generation of a MessageType condition within their subscriptions. Internally, BizTalk defines the message with an XSDPart, rather than a DotNetPart, part. there is therefore a subtle, and rather confusing difference in the two approaches. The 'Any' XSD schema is strictly more appropriate when handling XML messages only. The System.Xml.XmlDocument approach is a .NET, rather than an XSD, approach to typing the message part, and so is more appropriate when handling non-XML classes! In reality, it makes little difference which technique you use, and you are best advised to follow Richard's example, as this is the only approach which is documented.
I can't resist adding one more nugget. This comes with a serious health warning, and I am being rather irresponsible in handing this on. There is an entirely undocumented, black-belt, technique you can use to access the raw content stream of a message part when using a type-less approach in BizTalk. I have managed to get something of a reputation in SolidSoft in recent months for proposing the use of such dangerous techniques in production code - a suggestion which I am glad to report my wise and experienced colleagues have resisted gently but firmly! So, if you really want some rope to hang yourself with, here is the technique.
Pass your 'type-less' message to some helper code using an XLANGMessage parameter in your C#/VB.NET/J#. Then use the following code to access the stream (the XLANGMessage parameter is called 'wrappedMsg'):
BTXMessage innerMsg = (BTXMessage)wrappedMsg.GetType().GetMethod
( "Unwrap"
, BindingFlags.Instance | BindingFlags.NonPublic
).Invoke(wrappedMsg, null);
string encodingName = "";
System.IO.Stream contentStream = innerMsg.BodyPart.Value.GetStream(ref encodingName, true);
This code uses reflection to invoke the private Unwrap method defined on the abstract BTXMessage class, derived from XLANGMessage! You must unwrap the inner message using this technique because the wrapper has been carefully constructed by Microsoft to prevent you getting the stream directly. I make NO GUARANTEES about the safety of this approach. If you are foolish enough to use it, you are on your own if BizTalk blows up in your face.
If you are wondering, I recently proposed the use of a similar technique in order to wrestle control of the MessageType property back from BizTalk. The inability to control the setting of the MessageType property in message context for 'type-less' messages sent from orchestrations is a real problem in certain scenarios, and restricts the natural use of the subscription mechanism to implement more advanced patterns. This is a deliberate restriction, explicitly coded into BizTalk, but I consider it unnecessary. I haven't checked this out in BizTalk 2006, but I have seen no suggestion yet that this has been changed in the new version. My proposal was understandably rejected, and we had to use an alternative loop-back adapter approach instead. This was inelegant and a little inefficient.
[Additional Info] In BizTalk Server 2006, it is no longer necessary to consider using the above technique in order to set the MessageType property on a message within an orchestration. This is because you can now invoke pipelines directly from within your orchestration. For example, if you are processing an XML message, you can simply pass it to a Receive pipeline containing the XML Disassembler component. It will be returned to you with the MessageType property set. Alternatively, you could create a simple pipeline component to set the value of this property.
It is probably a little more efficient to use the undocumented technique, but I recommend staying within the confines of 'legality'.