Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / biztalk

BizTalk ESB Exception Handling – Consuming WCF Services – Part II

5.00/5 (1 vote)
4 Nov 2012CPOL8 min read 19.3K   138  
Managing exceptions when consuming WCF services via the BizTalk ESB Toolkit - Part II

Download Example Solution 

Introduction

I my last article I gave a way of handling exceptions that occur when consuming WCF services using the ESB Toolkit. That was an itinerary only solution. Today I want to show another approach that uses a combination of itineraries and orchestrations.



The Problem - Catching Exceptions

Let me describe again, the problem I was having. I’m consuming a WCF service from BizTalk using the ESB Toolkit. And, when an exception occurs, I am unable to handle the error elegantly. In fact, the full error is relayed back to the consumer of my service without me being able to stop it.

 

Solving the problem

The three ways of solving the problem that I could identify are:

  • Go back to using orchestrations
  • Use a hybrid solution of itineraries and orchestrations
  • Dive into the WCF stack and force the WCF adapter to do what I want it to do.

Today I will be looking at the second option. For the WCF stack option, see part I of this series.

 

The Solution – A Generic Orchestration

During my initial introduction to the ESB Toolkit I was looking at the samples it came with. In particular, I was diving into the Scatter-Gather example and discovered an orchestration that does much of what I we will be looking at today. It uses the resolvers of the calling itinerary to Scatter messages to other services. So, from this example I though to myself, let me see if I can write a generic itinerary processing orchestration that I can use anywhere. As it turns out, you can.

This is how I built the solution…

 

Step 1 – Helper Classes

I have built a MessageFactory helper class that creates SOAP Faults for when we trap exceptions in our orchestration. It uses the ResponseBodyWriter class from the part I to build the SOAP Fault. Here’s how:

C#
//Prepare to use our body writer to create a message
StringBuilder stringBuilder = new StringBuilder();
 
//Create the SOAP Fault body
using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
{
    using (XmlDictionaryWriter xmlDictionaryWriter =
                    XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter))
    {
        //Create an instance of our body writer
        ResponseBodyWriter bodyWriter = new ResponseBodyWriter("soap:Client",
                                                               faultString,
                                                               action,
                                                               errorMessage,
                                                               exception);
 
        Trace.WriteLine("[ESB MessageFactory] Creating SOAP Fault");
 
        //Execute the body writer
        bodyWriter.WriteBodyContents(xmlDictionaryWriter);
 
        //Flush the stream
        xmlDictionaryWriter.Flush();
    }
}
 
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(stringBuilder.ToString());
 
return xmlDocument; 

This helper also has a method that promotes message properties for us. We use this primarily to ensure that the message type is promoted when we send messages from our orchestration. We use the standard XMLReceive pipeline to do the job as described in this article by Ronald Lokers here.

C#
// See http://ronaldlokers.blogspot.co.nz/2012/04/promoting-messagetype-property-on.html
public static void PromoteMessageProperties(XLANGMessage message)
{
    Trace.WriteLine("[ESB MessageFactory] Promoting message properties");
 
    //Use the XMLReceive pipeline to promote the messaeg properties.
    //The property we are most interested in is the Message Type
    ReceivePipelineOutputMessages pipelineMessages = XLANGPipelineManager.ExecuteReceivePipeline(
                typeof(Microsoft.BizTalk.DefaultPipelines.XMLReceive),
                message);
 
    //Force the pipeline to act
    pipelineMessages.MoveNext();
    pipelineMessages.GetCurrent(message);
}

 

Step 2 – Create the Orchestration

Note: My sample is built using BizTalk 2009, Visual Studio 2008 and the ESB Toolkit 2.0.

 

Orchestration overview

The orchestration grabs a message from the message box and loads the associated itinerary from the context properties of the message. From the itinerary, it finds all the resolvers of the current itinerary service. It loops through each resolver performing their actions until either there are no more resolvers or an error occurs.

The logic I have used is based on there being a result message from the last resolver operation. So, for the first resolver to work, I copy the inbound message to my temporary result message. This concept is also used after a transform step. The copying of messages like this is not something I like doing as it adds an overhead of duplicate messages and all the performance problems there of. However, it’s a cost taken at the benefit of having a generic solution.

 

1 – Adding references

You’ll need to add the following references to your assembly:

  • Microsoft.XLANGs.Pipeline
  • Microsoft.Practices.ESB.Adapter
  • Microsoft.Practices.ESB.ExceptionHandling
  • Microsoft.Practices.ESB.ExceptionHandling.Schemas.Faults
  • Microsoft.Practices.ESB.Itinerary
  • Microsoft.Practices.ESB.Itinerary.PipelineComponents
  • Microsoft.Practices.ESB.Itinerary.Schemas
  • Microsoft.Practices.ESB.PipelineComponents
  • Microsoft.Practices.ESB.Resolver
  • Microsoft.Practices.ESB.Transform

2 – Messages and variables

Here are the messages I have used: 

Message Name<o:p>

Type<o:p>

Use<o:p>

mInboundMessage<o:p>

Microsoft.XLANGs.BaseTypes.Any<o:p>

The original message received<o:p>

mOutboundMessage<o:p>

Microsoft.XLANGs.BaseTypes.Any<o:p>

The final message returned to the itinerary<o:p>

mSendMessage<o:p>

Microsoft.XLANGs.BaseTypes.Any<o:p>

The message that gets sent to sub-services<o:p>

mReplyMessage<o:p>

Microsoft.XLANGs.BaseTypes.Any<o:p>

The response message from a sub-service<o:p>

And these are my variables: 

Variable Name<o:p>

Type<o:p>

Use<o:p>

vItinerary<o:p>

Microsoft.Practices.ESB.Itinerary.SerializableItineraryWrapper<o:p>

Holds the itinerary<o:p>

vItineraryStep<o:p>

Microsoft.Practices.ESB.Itinerary.SerializableItineraryStepWrapper<o:p>

Holds the current service/step in the itinerary<o:p>

vResolvers<o:p>

Microsoft.Practices.ESB.Itinerary.ResolverCollection<o:p>

Holds all the resolvers in the current step<o:p>

vResolver<o:p>

System.String<o:p>

The current resolver being executed<o:p>

vResolverDictionary<o:p>

Microsoft.Practices.ESB.Resolver.ResolverDictionary<o:p>

The settings from the resolver<o:p>

vResolverNumber<o:p>

System.Int32<o:p>

For looping<o:p>

vTransformType<o:p>

System.String<o:p>

For transforming messages<o:p>

vMapCLRType<o:p>

System.Type<o:p>

vTransportType<o:p>

System.String<o:p>

For message routing<o:p>

vTransportLocation<o:p>

System.String<o:p>

3 - Receive-Send Port and shape

The orchestration has a single Receive-Send port with both receive and send message types being of type Microsoft.XLANGs.BaseTypes.Any. It is a direct port so that it uses the message box to receive messages.

Note: all the messages in the orchestration are of xs:any type because it means the orchestration can be generic and process messages of any type.

 

4 – Receive shape

The receive shape has a filter expression so that it grabs the correct messages from the message box. Here is what I have used.

C#
(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceName == "ProcessItinerary") &&
(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceType == "Orchestration") &&
(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceState == "Pending") &&
(Microsoft.Practices.ESB.Itinerary.Schemas.IsRequestResponse == true)

Note: the ServiceName is configured in the itinerary by selecting the service from a dropdown. The dropdown is populated from the esb.config file. So, the name you use in the esb.config file must be the same as the filter value above. 

Here is what the start of the orchestration looks like:

 

Image 1




5 – Load the itinerary step

When we have received a message, we load the itinerary that comes with it in the context properties. This is how:

C#
vItinerary = new Microsoft.Practices.ESB.Itinerary.SerializableItineraryWrapper();
vItineraryStep = new Microsoft.Practices.ESB.Itinerary.SerializableItineraryStepWrapper();
 
// Get the itinerary and itinerary step
vItinerary.Itinerary = Microsoft.Practices.ESB.Itinerary.ItineraryOMFactory.Create(mInboundMessage);
vItineraryStep.ItineraryStep = vItinerary.Itinerary.GetItineraryStep(mInboundMessage);

 

6 – Get all the resolvers

From the itinerary step, we grab all the resolvers:

C#
// Retrieve the Resolvers associated with the itinerary step
vResolvers = vItineraryStep.ItineraryStep.ResolverCollection;
vResolverNumber = 0;

These steps look like this :

 

Image 2




Note: If we don’t have any resolvers, I throw an exception which is converted into a SOAP Fault later.

 

7 – Create our first result message

We create a copy of the inbound message so that we can run our first resolver a bit later.

C#
// Create a temporary reply message from the inbound message
//  - we do this to ensure we have a valid message in the next
//    transform stage
mReplyMessage = mInboundMessage;
mReplyMessage(*) = mInboundMessage(*);

 

8 – Execute the resolver 

Then we loop through each resolver and perform its tasks. The first step is resolve the resolver and to then grab it’s settings. Here’s how:

C#
// Get the current resolver
vResolver = vResolvers.Current;
vResolverNumber = vResolverNumber + 1;
 
// Pass the resolver configuration to the Resolver manager for resolution
vResolverDictionary = Microsoft.Practices.ESB.Resolver.ResolverMgr.Resolve(mInboundMessage, vResolver);
 
// Set transform properties
vTransformType = vResolverDictionary.Item("Resolver.TransformType");
 
// Set transport properties
vTransportLocation = vResolverDictionary.Item("Resolver.TransportLocation");
vTransportType = vResolverDictionary.Item("Resolver.TransportType");

 

Image 3

 

9 – Perform transforms

If the resolver has a transform type, then perform the transform: 

C#
// Retrieve map type from resolution structure
vMapCLRType = System.Type.GetType(vTransformType);
 
// Use XLANG transform keyword to execute the map
//  -we transform the message we got from the last resolver
transform(mSendMessage) = vMapCLRType(mReplyMessage);
mSendMessage(*) = mReplyMessage(*); 

 

Image 4


Note: I copy the result of the from the transform to the reply message so that it is available to the next resolver.

 

10 – Route the message

When the transport type and location are known, we will send the message to the sub-service.

C#
// Create the send message
//  - we use the message we got from the last resolver
mSendMessage = mReplyMessage;
mSendMessage(*) = mReplyMessage(*);
 
// Use the resolver to set the endpoint properties
Microsoft.Practices.ESB.Adapter.AdapterMgr.SetEndpoint(vResolverDictionary, mSendMessage);
 
// Promote the message type
ESB.ExceptionHandling.ServiceModel.MessageFactory.PromoteMessageProperties(mSendMessage);
 
// Set delivery port address
SendMessage(Microsoft.XLANGs.BaseTypes.Address) = vTransportLocation;
SendMessage(Microsoft.XLANGs.BaseTypes.TransportType) = vTransportType;

 

Image 5

 

11 – Send port details

We use a dynamic Request-Response port to send the message and receive a response. This way, the port can be configured by the itinerary 

 

12 – Advance the itinerary

When there are no more resolvers to process, we create our final result message and advance the itinerary to the next service. Here is how:

C#
// Copy the last reply message to the outbound message
mOutboundMessage = mReplyMessage;
mOutboundMessage(*) = mReplyMessage(*);
 
// Set message propertes so that the message is not picked up by the
//  orchestration again
mOutboundMessage(Microsoft.Practices.ESB.Itinerary.Schemas.IsRequestResponse) = mInboundMessage(Microsoft.Practices.ESB.Itinerary.Schemas.IsRequestResponse);
mOutboundMessage(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceName) = mInboundMessage(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceName);
mOutboundMessage(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceType) = mInboundMessage(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceType);
mOutboundMessage(Microsoft.Practices.ESB.Itinerary.Schemas.ServiceState) = "Complete";
 
// Promote all message properties
ESB.ExceptionHandling.ServiceModel.MessageFactory.PromoteMessageProperties(mOutboundMessage);
 
//Call the Itinerary helper to advance to the next step
vItinerary.Itinerary.Advance(mOutboundMessage, vItineraryStep.ItineraryStep);
vItinerary.Itinerary.Write(mOutboundMessage);

 

Image 6

 

13 – Return the response 

The final step is to return the response message to the message box to be processed by the next itinerary service.

 

14 – Exception Handlers 

To catch exceptions, I use a scope with exception handlers. In the exception handers I create a SOAP Fault message from the exception by using my message helper class mentioned above.

C#
// End the loop to stop processing any other resolvers
vResolverNumber = vResolvers.Count;
 
// Build Soap Fault message
mReplyMessage = ESB.ExceptionHandling.ServiceModel.MessageFactory.CreateSoapFaultMessage(exSoapException);
mReplyMessage(*) = mInboundMessage(*); 

 

Image 7 

Step 3 – Deploying The Orchestration

To deploy, just compile the assembly, add it to the GAC and deploy it to BizTalk.

 

Configure the send port configuration in the BizTalk console to use the standard XMLTransmit and XMLReceive. It won’t be executing any steps in the itinerary so don’t use any of the ESB Toolkit pipelines.

Now, to make the orchestration available to the ESB Toolkit and Visual Studio, open the esb.config file and add a new Itinerary Service as below. You can find the esb.config file here:

%Program Files%\Microsoft BizTalk ESB Toolkit 2.0\esb.config

XML
<configuration>
    <!-- ... -->
    <esb>
        <!-- ... -->
        <itineraryServices>
            <!-- ... -->
            <itineraryService id="{your own GUID>}" name="{service name}"
                         type="{Full type name of orchestration}"
                         scope="Orchestration"
                         stage="None" />
            <!-- ... -->
        </itineraryServices>
        <!-- ... -->
    </esb>
    <!-- ... -->
</configuration>
Here is what I have used for my sample.
XML
<itineraryService
        id="3CCA3815-E5F3-47a7-B864-7644E1A39087"
        name="ProcessItinerary"
        type="ESB.ExceptionHandling.ProcessItinerary, ESB.ExceptionHandling,
              Version=1.0.0.0, Culture=neutral, PublicKeyToken=a496c9267b296339"
        scope="Orchestration"
        stage="None" /> 

Remember to restart IIS and all BizTalk hosts to pick up the config changes.

 

Step 4 – Building your itinerary  

I took the itinerary from part I and changed it to use my orchestration rather than an off-ramp. Here is what looks like now. The change is that we have an orchestration extender with a single resolver that will route our message for us.



Image 8 

 

The “Route to WCF Service” resolver has been   configured to use a WCF-WSHttp transport type. Here are the settings I have used. You can use the BRE as I have for my customer but for this example I’m going static:

Property<o:p>

Value<o:p>

Resolver Implementation<o:p>

Static Resolver Extension<o:p>

Message-Exchange Pattern<o:p>

Two-Way<o:p>

Transport Name<o:p>

WCF-WSHttp<o:p>

Transport Location<o:p>

http://nowhere/nowhere.svc<o:p>

Action<o:p>

AnAction<o:p>

Target Namespace<o:p>

http://namespace.org<o:p>

Endpoint Configuration<o:p>

CloseTimeout=00:00:10&OpenTimeout=00:00:10&SecurityMode=None&SendTimeout=00:00:10<o:p>

I have set the endpoint timeouts to be low so that I can test the orchestration without waiting the default timeout for an error to occur. 

Once you have your itinerary, you can deploy it as normal and you’re ready to test.

 

Step 5 – Testing 

I have been using soapUI to test my service and the result I get is below. It’s in the format on my response schema because the last step of my itinerary transforms the SOAP Fault I get from my orchestration for me.

XML
<ns0:Response xmlns:ns0="http://ESB.ExceptionHandling.ResponseMessage">
    <ResponseElement1>An unexpected error occured</ResponseElement1>
    <ResponseElement2>System.TimeoutException: The HTTP request to 'http://nowhere/nowhere.svc' has exceeded the allotted timeout of 00:00:09.9950000. The time allotted to this operation may have been a portion of a longer timeout.</ResponseElement2>
    <ResponseElement3/>
</ns0:Response>

Note: at the moment my sample leaves a suspended message in BizTalk. I have overcome this in the past but haven’t had the time to resolve this error yet. When I do, I’ll update the sample.

 

Points of Interest

The strength of the ESB Toolkit does not only lie in the realms of the BizTalk pipeline. It can also be extended to orchestrations with very little effort.

 

The one downside it that the orchestration copies of messages a lot and this comes with a performance impact. 

However, I have used this generic orchestration to create services that don’t consume other services but simply transform messages for me. I can do this very quickly by building the right itinerary and deployment is a breeze.

 

History

Version 1.0
Version 1.1 - Clean up of the tables for easier reading


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)