Introduction
The other day, a colleague raised a 'problem' that turned out to be no problem at all. They wanted to invoke a web service from within an Orchestration. The web services had been created using .NET, and the WebMethod they wanted to call accepted no parameters:
[WebMethod]
public Customer getCustomer ()
{
...
return customer;
}
Having provided their BizTalk project with a reference to the web service, he then tried to work out how to construct an 'empty' message that could be sent to the .asmx file to invoke the WebMethod. I wasn't standing over my colleague's shoulder, but I would guess that he probably created a message, typed it using the correct Web Message Type, dropped a Message Assignment shape onto the Orchestration (which, of course, would be contained inside a Construct Message shape) and then tried to work out how to initialise the message within the expression code. Every attempt failed.
The answer of course, is very simple. A Web Message Type is really a Multi-part Message Type, and in this case the type defined a multipart message with no parts! BizTalk insists that Multi-part messages have at least one part (designated the Body part), but will make an exception for Web Messages. All that was needed was an empty Construct Message shape configured to construct the message. This would create the necessary 'empty' message which could then be used to invoke the WebMethod. It took me a second look before I realised where he was going wrong because, like him, I had got used to writing expressions in Message Assignment shapes to initialize messages. In this case, however, the message needed only to be constructed. No initialisation is required.
Message initialisation often comes as a surprise to developers new to BizTalk Server 2004. An understanding of what happens behind the scenes can be useful, and this article explains BizTalk's behaviour in this respect. In a sense, this subject is the 'ABC' of Orchestration development. However, some aspects are not very clearly explained within the documentation, and even experienced BizTalk 2004 developers (do any exist yet?) may not understand some of the finer points.
Message and Part classes
Within an Orchestration, BizTalk Server 2004 uses .NET classes to represent individual messages. These classes act as generic wrappers around the underlying message data, and are quite straightforward. Each message class is auto-generated in C# as a derived Microsoft.BizTalk.XLANGs.BTXEngine.BTXMessage type. This class is not documented, but is derived as follows:
System.Object
Microsoft.XLANGs.BaseTypes.XLANGMessage
Microsoft.XLANGs.Core.XMessage
Microsoft.BizTalk.XLANGs.BTXEngine.BTXMessage
The base XLANGMessage class is documented, and is the type generally used when passing messages out to your own code. Each XLANGMessage is a collection of Microsoft.XLANGs.BaseTypes.XLANGPart objects. Orchestrations use the Microsoft.XLANGs.Core.DotNetPart class derived as follows.
System.Object
Microsoft.XLANGs.BaseTypes.XLANGPart
Microsoft.XLANGs.Core.Part
Microsoft.XLANGs.Core.DotNetPart
Again, the DotNetPart class in undocumented, while the XLANGPart class is documented and generally used within your own code.
The auto-generated message classes not only provide an enumerable collection of parts, but also provide each part as a public field. Each part has a LoadFrom() method which populates the part object with data. This method accepts any object as a parameter, but can only populate the part from certain types, such as built-in .NET types, streams, XmlDocuments and XmlElements.
When you use a message programmatically within an XLANG/s expression, you are essentially using an abstract representation of a BTXMessage object. XLANG/s imposes additional constraints over the use of this class and hides details like the LoadFrom() method from the programmer. There are also some peculiarities with regard to the way parts may or may not be represented in IntelliSense. We will explore this later. You can pass messages directly between XLANG/s and external .NET code using method parameters and return values data typed as BTXMessage.
Message Construction and Intialisation
'Construction' of messages refers to the underlying instantiation of BTXMessage classes that represent messages to the Orchestration engine. Messages are constructed indirectly using the Construct Message shape (obviously!). All you need to do to create the instantiation code is to drop a Construct Message shape onto an Orchestration and assign the list of messages you wish to construct to its Messages Constructed property. This causes the emission of C# code behind the scenes that looks like this:
__messagetype_zzTest_test __msgTest = new __messagetype_zzTest_test("msgTest", __ctx1__);
__msgTest is the behind-the-scenes variable used to store an instance of the __messagetype_zzTest_test class which is derived from BTXMessage. Remember always that XLANG/s is merely an abstraction of the generated C# code. In this case, the XLANG/s message is named 'msgTest'. The BTXMessage constructor creates Part object instances and assigns these to the public fields.
'Initialization' of messages is quite distinct to construction, and refers to the initialization (not the instantiation) of the message part objects. Until message parts have been fully initialised, the message cannot be used within the Orchestration, and BizTalk will refuse to compile your code. Of course, if a message has no parts, there is no initialisation to do.
Message Typing in XLANG/s
Messages can be typed in a variety of ways in XLANG/s. These types do not affect the underlying .NET types used in the Orchestration code, but do impose higher-level constraints on the content of message parts. There are three message types: .NET Classes, Schemas and Multi-part Messages. A fourth type, Web Messages, is really just a Multi-part Message type used in conjunction with SOAP-based web services.
Multi-part Messages
For Multi-part messages, each individual part is exposed to the developer using IntelliSense. The parts appear as 'properties' of the message, and you can initialise each part in a straightforward manner depending on the data type selected for that part. As noted above, Web Message Types are Multi-part Message Types. The part 'properties' you see in IntelliSense are not true properties, but do map to the underlying public fields of the BTXMessage class. When you assign a value to a part, C# sharp code is emitted that invokes the LoadFrom() method of the Part object assigned to the corresponding public field:
__msgTest.partA.LoadFrom("Hello World");
One aspect of multi-part messages which I shall touch on briefly here is that they must have at least one part designated as the 'body' part (this rule is relaxed for web messages which, as mentioned above, can contain no parts). Within an Orchestration, it is not generally important to know which part is the body part, but this may become a critical issue when the message is passed through a pipeline. In pipelines, messages are accessed via the IBaseMessage and IBaseMessagePart interfaces. The IBaseMessage interface defines a 'BodyPart' property, and although other parts can be accessed programmatically, the components and functionality provided by Microsoft generally only use the designated body part for features such as routing and tracking. The SOAP adapter, which transports web messages, does not depend on the existance of body parts.
Schema Messages
If you type a message using a Schema, things are very similar. In this case, the underlying BTXMessage class has a single part field. It is essentially equivalent to a Multi-part message with a single Part. However, there are two important differences:
- IntelliSense does not display the single part. To initialize the part, you assign a value directly to the message itself in your XLANG/s expression. This inconsistency between Multi-part and Schema messages is confusing to new BizTalk developers.
- The message part is restricted in the data types that can be used to initialise it. You can use an existing message of the same type, or an instance of either the XmlDocument or XmlElement classes. Note that you cannot use Stream objects for this purpose within an XLANG/s expression.
.NET Class Messages
Messages typed as .NET classes are very similar to Schema messages. They contain a single part which is not shown in IntelliSense. In XLANG/s you assign an instance of the required .NET class directly to the message. .NET classes used in this way must be capable of serialization using the XmlSerializer facilities of the .NET framework. They may, in addition, be associated with a BizTalk custom formatter for non-XML serialization. I discussed this to some extent in an earlier post about the SMTP adapter.
Property Promotion
Further confusion can arise over the issue of promoted properties. In the XSD schema, you can promote fields which contain singleton values. There are two types of property promotion; distinguished fields and property fields.
For Schema message types, distinguished fields are schema 'fields' which are promoted through the use of additional annotation within the XSD schema. XSD annotations provide an extensibility mechanism that allows additional domain-specific schema semantics to be expressed in a machine-readable fashion.
Messages typed using .NET Class messages also allow distinguished fields. In this case, fields are the public fields and properties of the class definition. In order to distinguish a field, you need to define the class yourself in a language such as C#, and add the DistinguishedField attribute to the class fields and properties that you wish to distinguish.
Multi-part message types are essentially similar, but with the IntelliSense twist described earlier. It is important to understand that a message part is not the same as a field. Consider a Schema message type. This has a single part that contains an XML document. Fields are nodes within that XML. The same approach applies to Multi-part message types. A single part may be typed as either a .NET class or as a schema. That class or schema may define distinguished fields. The twist is the way this is represented in XLANG/s IntelliSense. IntelliSense works differently for Multi-part messages than for other message types. In a Multi-part message, each part is displayed as a 'property' of the message. If the schema or class for that part defines distinguished fields, these are then shown in IntelliSense when you type the dot separator after the part name. Hence you might have XLANG/s code similar to the following:
msgMultiPart.PartA.ADistinguishedField = "Hello world";
Behind the scenes, BizTalk would generate the following C# code:
__msgMultiPart.PartA.SetDistinguishedField("ADistinguishedField", "Hello world");
As we have seen, in other message types the single message part is never displayed in IntelliSense. Distinguished fields are shown as 'properties' directly of the message itself, although they are really associated with the single message part. To initialise a multi-part message within an Assign Message shape, simply assign values to each of the parts. For Schema message types you must assign an XmlDocument or XmlElement directly to the message, and for .NET Class messages, you must assign an object instance to the message. Once the part has been initialised, you can then go on to initialise individual distinguished fields if you so wish.
Every message that flows through BizTalk has contextual data associated with it. This contextual data includes message properties. Properties are defined and stored externally to the message, but are associated with the message at runtime. Note the subtle, but important difference between distinguished fields and property fields. A distinguished field is a specially identified field within a message part, while a property field is contextual data associated with the message itself, rather than an individual message part. It is simply a rather confusing peculiarity of IntelliSense that distinguished fields appear to be message-level properties for Schema and .NET Class messages.
You use Schemas and .NET Classes to define the layout of message parts (just like WSDL), rather than messages. Hence, for contextual Property Fields, BizTalk cannot place the definitions of these properties within individual schemas or classes. Instead, you must create an additional Property Schema. You can then promote individual fields within a part by mapping the field definition in the schema or class to the property definition in the Property Schema. At run-time, the promoted data within individual message parts becomes accessible at the message level.
XLANG/s provides a special syntax for accessing message properties. You use a programmatic identifier within parenthesis immediately after the message name. Hence you might have code that looks like this:
msgMultiPart(APropertyField) = "Hello world";
The 'APropertyField' message property might be mapped to a single node inside the XML contained in on the message parts.
If you want to promote a field within a .NET class as a property field, use the Property attribute within your class definition in front of your public property or field definition.
Conclusion
The subject of this article is one of the most basic subjects within BizTalk Orchestration development. However, some of the finer points are not well explained within the documentation. A detailed knowledge of how messages, message parts and promoted fields are handled within BizTalk Server 2004 is indispensable to BizTalk developers.