Challenge: Execute a Map and Return the Result, WITHOUT an Orchestration

The Issue

Earlier this week Bennie Wentzel, a developer from a customer I was engaged with last year, posed me a question which went something like this:

If I have a synchronous web service receive port, how can I return the result of a map on this receive port to the calling web service client, without using an orchestration or the ESB Guidance Toolkit ?

Although I had never done this before I was pretty sure it was possible, so I took this as a challenge and set out to prove that it could be done.  After some tinkering I had the solution, which I have used as the excuse for this post.  As a bonus, the pipeline used to accomplish this task has been implemented in a generic manner, so that it can be used in any situation requiring configuration-based changes to be effected on a message's context properties within a receive pipeline.

The Investigation

The first step in finding a solution to this problem is to get a much deeper understanding of the requirement.  In most BizTalk solutions that use a request-response receive port, the request message is used to start an orchestration or it is relayed to a solicit-response send port.  When the orchestration or send port provide the response message, this message is then routed back to the calling service/application that submitted the original request message.  The requirement expressed within Bennie's question is that he wants to be able to use BizTalk's schema resolution, receive-side map resolution, and map execution logic to ensure that the output of the executed map is returned to the original caller.  In this scenario, the use of an orchestration or an intermediary send port would add unnecessary overhead.

The solution to this problem rests in understanding how BizTalk matches a response message to an initial request message.  When BizTalk processes a message through its receive-side architecture (receive adapter, pipeline, map) it identifies if the receive port is as a request-response port, and if it is then and instance subscription is created at the time that the request message is placed in the MessageBox.  This instance subscription is composed of the same EpmRRCorrelationToken promoted property that is in the request message's context, and a RouteDirectToTP promoted property with a value of True.  Any message that has the matching values in these two promoted properties will then be routed back through BizTalk's send-side architecture (map, pipeline, adapter) as the response message for the initial request.

To see this in practice, do the following:

  • Create a request-response receive port.  One way to do this is to use the BizTalk WCF Service Publishing Wizard, and to have it create the receive port and receive location for you.
  • Enable the request-response receive port.
  • Create a send port that writes out to file, and has a Filter where BTS.ReceivePortName is equal to the name you gave to the receive port in the first step.
  • Start this send port.
  • Send a message to the receive port.  (A nice utility I use for sending a message to a SOAP/WCF service is SoapUI, but you could also use the WSDL from the service to build an InfoPath form)
  • The request message should now be routed to the send port, and the request-response receive port will be waiting for its response message.  As no response message will be returned at this stage the client will eventually time out, but during this period we have an opportunity to see what the instance subscription BizTalk has created looks like.
  • From the Group Hub Page, select the "New Query" tab, and select "Subscriptions" for the "Search For" field name.
  • Add a new field name "Subscription Type" and set this equal to "Instance Subscription".  Your new query should now look like this:
     GroupHub_NewQuery
  • Execute the search, and you should see a result like the one shown below:
    GroupHub_NewQueryResults 
  • If you look at the details of the instance subscription by double-clicking on it, and select the "Expression" tab, you will see that BizTalk has created a subscription based on the EpmRRCorrelationToken and the RouteDirectToTP context properties, as you can see in the screenshot below.
    GroupHub_Subscription

If you then also look at the context properties of the request message, you will notice that the value of the EpmRRCorrelationToken is the same as the value used in this instance subscription, and it is already promoted.  The request message does not, however, contain the RouteDirectToTP context property.  (For more info on these properties you can review the following MSDN page: https://msdn.microsoft.com/en-us/library/ms966048.aspx.)

The Solution

Having identified the problem, the solution becomes obvious: we need to add and promote the RouteDirectToTP context property, with a value of True.

The only point of extensibility we have available to use to do this is the receive pipeline.  If we can add the RouteDirectToTP context property, set its value to True and promote it within the pipeline, then the message coming out of the receive-side process will be a match to the instance subscription, and the message will be returned via the response portion of the request-response port. 

Not wanting to reinvent the wheel I embarked on a little Internet surfing, where I came across this post, which provided the code I needed to accomplish this.  Using this code as a starting point I made a few modifications (to support creating a property if it did not exist, and to be able to specify the Type to use when setting the value of the promoted property), and ended up with a version of this pipeline component which can be used when you want to create a new distinguished or promoted property, or when you want to set a value for an existing distinguished or promoted property, or when you want to use a regular expression to change the value of an existing distinguished or promoted property.  You can download the code for the ModifyMessageContext pipeline component here.  (NOTE: While this component is functional it is not of a commercial standard, in that there is no value validation, no tracing, etc. - so use at own risk).

I then created a receive pipeline using the custom pipeline component and the standard XML Disassembler component, and I set this pipeline as the receive pipeline to be used for the request-response receive port.  On the receive port I then set the properties of the custom pipeline component as shown in the screenshot below.  (The namespace is set to https://schemas.microsoft.com/BizTalk/2003/system-properties, as per the Expression in the screenshot above.)  Lastly, I deleted the send port created in the earlier section, as it was no longer required.

PipelineMessageContext1

At this stage I had not setup a map on the receive port, so when I sent a message to the request-response receive port I got the same message returned.  Getting the map to execute and then have the mapped result returned as the response was simply a matter of configuring the map on the receive port.  I ran one more test and was happy to see the output of the map being returned as the response message.

Summary

While BizTalk does not by default provide a mechanism whereby the output of the process involved in receiving a request message can be returned as a response message, it does create an instance subscription which is used to ensure that when a response message is available in the MessageBox, this response message is returned to the waiting client.  By using this instance subscription it is possible, to modify the message generated by the receiving process in a way that will ensure that the message placed into the MessageBox at the end of the receiving process has the detail required to match the instance subscription, and thus enable the output of the receiving process to become the response message.

This post showed how, with a custom pipeline and custom pipeline component, it is possible to ensure that the message placed into the MessageBox (after the receive pipeline and any receive-side map has executed) matches the instance subscription, and therefore gets returned as the response message.

The result is a generic method whereby a message can be sent to BizTalk, the schema can be resolved, a matching map can be executed, and the output of the map can then be returned via the send portion of the request-response receive port.  All without an orchestration!

Any comments / queries / suggestions / challenges are, as always, most welcome.