I received a great BizTalk question from a customer the other day, and wanted to build out the solution I proposed. Their primary
problem was how to correlate an untyped message back to a running orchestration.
There are a few ways you can try and do this, but I wanted to try and stick to the customer requirement of not adding
any data to the untyped message itself. Their use case involved receiving a message (of any format), sending it to an MSMQ for
pickup, then having an application read the message from the queue, allow the user to act upon it, and then submit the
message back to BizTalk via a web service. Interesting.
My solution uses a combination of a property schema, custom pipeline components, and SOAP headers. The first
part of my solution involved creating a project holding a property schema. The contained schema element was named TrackingID. It is
vital to set the node's Property Schema Base value to MessageContextPropertyBase. This means
that the promoted value comes from the message's context instead of the message body itself. Without setting this, the orchestration
would gag when trying to use this value for correlation.
The next step was to build a pair of custom pipeline components (using the great
pipeline wizard). I'll get to the second component a bit further down.
The first component, called TrackingIDPromoter, simply creates a unique ID, and throws it into the promoted
context of the message, like so ...
string trackCode = Convert.ToString(System.Guid.NewGuid());
inmsg.Context.Promote("TrackingID", "http://Microsoft.Demo.Customer.CustomerPropertySchema", trackCode);
Next, I have a BizTalk project holding my orchestration. The orchestration receives a message, sends a message out, and receives that same message back.
My message variable is of type System.Xml.XmlDocument, thus untyped and able to hold any valid XML content. I also created a Correlation Type that
points to Microsoft.Demo.Customer.TrackingID as the correlated property. And, I can also access this unique tracking value in
my message context by doing this ...
My customer can then take this value, and set it to the MSMQ label so that it can be extracted later.
Now, I want to take the port used to get the message back to my orchestration and expose it as a web service. But, I need
a way to also be able to pass that tracking code back in. So, I decided to create a SOAP header to store it. So, I built
a simple XSD schema with a root name WSHeader and containing a single element called TrackingID. When do
I use this? Well, when you walk through the Web Service Publishing Wizard, you select Add additional SOAP headers.
When you click Next, you get prompted to add the header. Here you choose an available schema (in my case, the WSHeader)
and continue on. Now when you look at the web service definition, you can see the header expected.
Now of course, the second custom pipeline component comes in. This one is used on the SOAP receive location, and takes the
value OUT of the SOAP header, and throws it back into my promoted TrackingID value, and thus allowing the message to
correlate back. This pipeline component's Execute method looks like this ...
string headerVal = inmsg.Context.Read("WSHeader", "http://schemas.microsoft.com/BizTalk/2003/SOAPHeader").ToString();
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
//get value of tracking code from soap header
string trackingID = doc.SelectSingleNode("//TrackingID").InnerText;
//promote value back into context
inmsg.Context.Promote("TrackingID", "TrackingID", "http://Microsoft.Demo.Customer.CustomerPropertySchema", trackingID);
Note that the namespace for the SOAP header was NOT the value in the header schema, but the standard SOAPHeader namespace.
So these custom pipeline components get thrown into a couple receive pipelines in my BizTalk project, and get deployed. Then,
I built a silly little helper app that sends an XML message back to this web service. When you create a reference to a
web service with a SOAP header, you also get an object for that header. So my code to submit to this service looked like ...
CustService.Microsoft_Demo_Customer_Orchestration1_Port3 svc = new MyService.Microsoft_Demo_Customer_Orchestration1_Port3();
CustService.WSHeader header = new CustService.WSHeader();
//set header value to queue label retrieved
header.TrackingID = queueLabelBox.Text;
svc.WSHeaderValue = header;
I still love the fact that you get all these nicely typed objects for web services. There's an object named after my SOAP
header, and it has a property I need. Great stuff.
So, now when I receive a message, my pipeline immediately stamps it with a unique ID (in the message context, not body).
When I send the message out, I initialize a correlation set using the promoted value. On the way back in, I throw that
value in the SOAP header, which my next pipeline yanks out and sticks back into context, thus enabling full correlation, while
not once mucking with (or even knowing the structure of) my base message. Neat huh?
Technorati Tags: BizTalk