Below is an idea how to improve SOAP exception handling in BizTalk to WCF communication. Let’s consider approach recommended in BizTalk SDK documentation (Catch Typed Fault Exception sample):
  1. Add custom WCF fault to the service contract and add corresponding exception handler in the BizTalk orchestration.
  2. In BizTalk orchestration add hander that expects System.Web.Services.Protocols.SoapException to catch any general SOAP exceptions
This allows catching any custom WCF faults and handling them gracefully. It also handles general SOAP exceptions but the downside is that SOAP exception caught will not be original SOAP fault that came over the wire from the service. It’s going to be secondary SOAP exception thrown by the adapter. Receive pipeline will try to match incoming message type using XPath expressions defined in the send port configuration. Since custom WCF faults are embedded as content of Detail node of the soap:Fault message there will be XPath expression like this: /*[local-name()=’Fault’]/*[local-name()=’Detail’]/* | /*[local-name()=’DivideResponse’], where the first part is matching any child of Detail element. In case if WCF service returns general SOAP fault it will put original exception in Detail element as ExceptionDetail node (QName: http://schemas.datacontract.org/2004/07/System.ServiceModel#ExceptionDetail). Since the schema for ExceptionDetail is unknown to pipeline it will throw SOAP exception with generic description and all original details will be lost. This secondary SOAP exception is the one that actually caught by our generic handler:
There was a failure executing the response(receive) pipeline: “Microsoft.BizTalk.DefaultPipelines.XMLReceive, Microsoft.BizTalk.DefaultPipelines, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35” Source: “XML disassembler” Send Port: “WcfSendPort_BatchEntityService_BasicHttpBinding_IBatchEntityService” URI: “http://100-bztlk-d-009.absg.net/Absg.eCommerce.Ordering.ServiceHost/BatchEntityService.svc” Reason: Finding the document specification by message type “http://schemas.datacontract.org/2004/07/System.ServiceModel#ExceptionDetail” failed. Verify the schema deployed properly.
One solution to this problem is to define http://schemas.datacontract.org/2004/07/System.ServiceModel#ExceptionDetail schema and include it as a fault contract for the WCF operation on the client side. That way all original SOAP fault details can be captured and propagated as ExceptionDetail fault. So that’s how I modified BizTalk orchestration in SDK sample:
  1. Declared new schema ExceptionDetail.
  2. Created new multi-part message type IOperation_Divide_SoapFault that has part of type ExceptionDetail
  3. Added to Divide operation new GenericSoapFault fault of type IOperation_Divide_SoapFault
  4. Added exception handler CatchSoapExceptionDetails for this new fault
Notice no changes required on the service side, all I had to do is on the client. But I did one thing in the service code to throw SOAP exception if numerator equal to 0 (just to be able to test such scenario):
if (numerator == 0)
throw new System.Web.Services.Protocols.SoapException(“Test exception”, new XmlQualifiedName(“A100”));
Dropping input file with 0 as numerator (DivideGenerateSOAPExceptionInput.xml) will create following message in the Fault folder:
<?xml version="1.0" encoding="utf-8"?><MyOperationException xmlns="http://schemas.datacontract.org/2004/07/Service" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Error>&lt;ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"&gt;&lt;HelpLink i:nil="true" /&gt;&lt;InnerException i:nil="true" /&gt;&lt;Message&gt;Test exception&lt;/Message&gt;&lt;StackTrace&gt;   at Service.Operator.Divide(Int32 numerator, Int32 denominator) in C:\Projects\TestBed\Typed Fault Exception Handling\WcfService\Program.cs:line 37
   at SyncInvokeDivide(Object , Object[] , Object[] )
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]&amp;amp; outputs)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp;amp; rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp;amp; rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc&amp;amp; rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc&amp;amp; rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc&amp;amp; rpc)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc&amp;amp; rpc)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)&lt;/StackTrace&gt;&lt;Type&gt;System.Web.Services.Protocols.SoapException&lt;/Type&gt;&lt;/ExceptionDetail&gt;</Error>
  <Operation>Divide</Operation>
</MyOperationException>

Note it contains exception detail section with original “Test exception” message.

Another approach would be to use exclusively SOAP faults for all kind of custom and system faults, reducing number of exception handlers in orchestrations to one. It can be achieved by implementing custom XML Disassembler pipeline component where incoming message would be inspected and if SOAP Fault is detected all properties can be copied to secondary SOAP fault thus preserving all original exception details.

Notice that we received original exception detail with the “test exception” in this message.