1.
Problem
You are using the
publish/subscribe method in your orchestration architecture with many
different schemas, and there are a number of common fields that need to
be passed with all documents delivered to the BizTalk MessageBox (using
direct binding on all ports). You want to be able to pass these fields
without adding them to all of the individual schemas.
2. Solution
Assume that you have three
XML instances schemas as follows (XML instances are shown for
clarification):
<Person>
<ID>ID_0</ID>
<TraceFlag>true</TraceFlag>
<Name>Name_1</Name>
</Person>
<Company>
<ID>ID_0</ID>
<TraceFlag>true</TraceFlag>
<CompanyName>CompanyName_2</CompanyName>
</Company>
<Address>
<ID>ID_0</ID>
<TraceFlag>true</TraceFlag>
<AddressLine>AddressLine_1</AddressLine>
</Address>
Two of the elements are
common in all three of the schemas and represent values that are common:
ID represents a common tracking ID, and TraceFlag represents whether logging should occur in an
orchestration. The elements are not truly part of the description of a Person,
Company, or Address, but their values
need to be available. Your goal is to move these common fields out of
the schemas so that they look as follows:
<Person>
<Name>Name_1</Name>
</Person>
<Company>
<CompanyName>CompanyName_2</CompanyName>
</Company>
<Address>
<AddressLine>AddressLine_1</AddressLine>
</Address>
All messages in BizTalk are
passed through the MessageBox wrapped in a SOAP envelope. A SOAP
envelope consists of two sections: the Header and the Body, wrapped in
an envelope, as shown in Listing 1. The Header can be common across all schemas,
while the Body contains the actual XML instance of any given schema.
Adding and accessing fields within orchestrations at the SOAP Header
level is made available in a simple and straightforward way. This is a
powerful and useful tool that has multiple applications depending on the
solution architecture.
Example 1. Sample SOAP Envelope Structure
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" <soap:Header> <MessageId>uuid:bbbb-cccc-dddd-eeee</MessageId> <TraceFlag>true</TraceFlag> </soap:Header> <soap:Body> <Person><Name>Name_1</Name></Person> </soap:Body> </soap:Envelope>
|
In Listing 1, the Header contains two fields and the Body
consists of an XML document that is an instance of the Person schema. This is an actual representation of what
a document looks like when placed in the MessageBox by an orchestration
when using the SOAPHeader
property schema and Person schema
described in this recipe.
By default, BizTalk will
strip the XML document out of the envelope's Body and deliver it to the appropriate subscribers. These
subscribers see the XML document that was originally contained in the
Body and do not have context to the Header. However, if a SOAPHeader property schema has been defined, the
subscriber will also have access to the envelope's Header information.
The SOAPHeader
property schema shows which fields will be promoted and available at the
Header level on all schemas within a given project. The SOAPHeaderhttp://schemas.microsoft.com/BizTalk/2003/SOAPHeader and have each of the elements on it with their Property
Schema Base set to MessageContextPropertyBase. The steps for creating a SOAPHeader
property schema are as follows:
property schema must reference the namespace
Add a
new schema to a BizTalk project.
In the
Properties window of the schema, set the Target
Namespace to the BizTalk SOAPHeader namespace and the Schema
Type to Property, as shown in Figure 1.
Add the desired elements to
the schema. There is no root node on a SOAPHeader property schema; it has only elements. In this example, there
would be two elements added to the schema: MessageId and TraceFlag.
See Figure 2.
On each element created, set the Property
Schema Base in the Properties window to MessageContextPropertyBase,
as shown in Figure 3.
Build and deploy the solution.
Once compiled, the elements in the SOAPHeader property schema will be available on all messages
within an orchestration and can be accessed and set in Expression
shapes and Message Assignment shapes. Listing 2 shows how to set these elements.
Example 2. Setting
Properties in a Message Assignment Shape
// populate the SOAP Header information strMessageId = "111-222-333-444"; blnTraceFlag = true;
msgAddress(CreatingSOAPHeaders.MessageId) = strMessageId; msgAddress(CreatingSOAPHeaders.TraceFlag) = blnTraceFlag;
msgPerson(CreatingSOAPHeaders.MessageId) = strMessageId; msgPerson(CreatingSOAPHeaders.TraceFlag) = blnTraceFlag;
msgCompany(CreatingSOAPHeaders.MessageId) = strMessageId; msgCompany(CreatingSOAPHeaders.TraceFlag) = blnTraceFlag;
|
NOTE
The value available in the
properties of the messages will be equal to the namespace given the SOAP
Header schema. In this project, it was set to CreatingSOAPHeaders; therefore, all of the properties will be
listed, as shown in Figure 4.
2.13.3. How It Works
The project accompanying this recipe (CreatingSOAPHeaders) demonstrates the practical application of the
SOAP Header. The basic architecture of the solution is this:
Primary
orchestration receives an incoming document from a file receive
location.
Orchestration
sets the promoted SOAP Header properties in a Message Assignment shape.
Orchestration delivers the message to the BizTalk
MessageBox via direct binding on a port.
A secondary orchestration subscribes to the
MessageBox and is instantiated when a document matching the schema it is
subscribing to arrives.
The secondary orchestration reads the values of the SOAP
Header and writes them to the Windows event viewer.
Figure 5 illustrates the flow of the primary
orchestration and the use of a Message Assignment shape to set the SOAP
Header properties before delivering to the MessageBox (port_2 is direct
binding).
The secondary
orchestration subscribes to the MessageBox and writes the values of the
SOAP Header properties to the Windows Event Log. Figure 6 shows the secondary orchestration.
How do you access
incoming documents wrapped in SOAP envelopes? Incoming documents can be
wrapped in a SOAP envelope (by an external client to BizTalk), but by
default, only those that are delivered via a SOAP transport receive
location will be automatically promoted and made available within an
orchestration.
For example, with
orchestrations exposed as web services, additional properties can be set
at the SOAP Header level using the BizTalk Web Services Publishing
Wizard. These properties are then available to any client who calls the
web service, accessible in the same way as all of the standard web
methods and properties. However, if the delivery of a message wrapped in
a SOAP envelope via a different transport is required (such as MSMQ or
file drop), you need to use a custom pipeline component.
Listing 3 demonstrates a process to promote incoming
values to the SOAPHeader property schema.
This example is a complete custom pipeline intended to be placed on the
decode stage of a BizTalk custom pipeline, followed by a standard XML
disassembler. It demonstrates how to do the following:
Load the
incoming message stream into an XML document, which can be accessed via
the Document Object Model (DOM).
Load the MessageId
value located at the Header level of the incoming SOAP envelope. The
incoming message matches the structure of the document shown in Listing 1.
Promote the field to the SOAP
Header, and make it available once the document is delivered to the
MessageBox.
Return
the document within the Body of the SOAP envelope as a stream.
Example 3. Pipeline Property Promotion
public IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg) { try { string strMessageId = "";
// declare XML Document System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
// prepare the fields to be promoted xmlDoc.Load(inmsg.BodyPart.Data);
// if values are not present, default to empty string try { strMessageId = xmlDoc.SelectSingleNode ("//*[local-name()='Header'/*[local-name()='MessageID']"). InnerText; } catch {}
// strip off the envelope and return just what is within the Body System.IO.MemoryStream ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes (xmlDoc.SelectSingleNode("//*[local-name()='Body']").InnerXml));
inmsg.BodyPart.Data = ms;
// promote the SOAP Header fields inmsg.Context.Promote("MessageId", "http://schemas.microsoft.com/BizTalk/2003/SOAPHeader" .ToString(),strMessageId);
} catch (Exception ex) { throw new Exception("Pipeline component exception - " + ex.Message); } return inmsg; }
|