Pages

Showing posts with label Soap. Show all posts
Showing posts with label Soap. Show all posts

Use Fiddler with BizTalk to monitor Incoming/Outgoing Traffic

Friday, June 7, 2013

Using Fiddler to debug BizTalk messages?

When using HTTP, SOAP or WCF send ports it can be incredibly useful to view the content of messages that are sent on the wire. This allows you to inspect the headers (SOAP or HTTP). Fiddler is a simple but fantastic tool to allow you to do this.

By default, Fiddler will not trace any messages sent to endpoints by BizTalk as it does not use WinInet. However, BizTalk send ports can be configured to use a proxy allowing Fiddler to intercept them. On the Send Port tick the Use Proxy checkbox and set the Server to 127.0.0.1 and the port to 8888. For dynamic ports, set the following properties (as applicable to the adapter being used)

// Debug via fiddler
msgSendRequest(SOAP.UseProxy) = true;
msgSendRequest(SOAP.ProxyAddress) = "127.0.0.1";
msgSendRequest(SOAP.ProxyPort) = 8888;



Note that this needs to be removed when Fiddler is not running since traffic directed to the proxy will not be received by anything.
Read more ...

Advanced Users Biztalk WIKI : Useful How to Links Part - 5

Tuesday, June 12, 2012
 Useful How to Links Part - 5

Bre Static Support Key

Calling Data Access from Ado.Net

Sso Configuration Storing Values

Xml Document XPath Sample

ESB Dynamic Routing using Itineraries


Read more ...

Handling Custom SOAP Exceptions in BizTalk

Monday, September 26, 2011
 Post by Romiko's Blog
Sometimes in Orchestrations you would like to throw a custom exception. Before we begin, remember this rule:
  • Never throw a general exception outside the catch block in an Orchestration
The rule above is a bit funny, because when you drag a default exception shape in a try block within an orchestration, it will default to General Exception and you cannot choose any other exception type.
Imagine we need to call a web service and then handle the response from it within an orchestration. However, sometimes I may get a timeout from the web service or I would only like to wait a 1 minute or so to get the response. What I usually do in these cases is create a listen shape after I have called the web service and then I use a delay shape to wait a specified amount of time for the web service response, if it does not respond within the time span , I simple throw a Timeout Exception.

However, as mentioned when you drag a default throw shape, the only option is to throw a general exception, to solve the problem, create a variable of type .NET Class
Then, navigate to the mscrolib library:
And choose the exception type as illustrated below.
Give the variable a friendly name in the Orchestration View
Add a throw exception under the Delay shape and assign the type of exception to the variable name you created (Weird I know, but this is how it is done)
Below are the properties of the Throw Exception Shape
As you can see above, I have a listen shape after sending a message to a web service and am now waiting for a response.
And you nearly done, what you then do is add a Catch Exception by right clicking the scope and clicking add exception handler:
  • Remember all transaction should be in a scope, so your catch blocks will automatically be appended to it. Now it is time to customize the catch block.
Right click the new Catch Block and set the type of exception to catch, in my case, a timeout exception.
There are two properties to set here:
  • Exception Object Type
  • Exception Object Name
In my case this will be
Exception Object Type = System.TimeoutException (I had to choose .NET Class and navigate through the mscrolib again)
Exception Object Name = e ( I chose e, as this is what we use in coding most of the time, you can choose anything you like)
So the Exception Object Name is similar to doing this:
catch (Exception ex)
{
}
This is how looks is now.

Don't forget to add custom code to handle the exception in the Catch Block, maybe to update a log table or send a message somewhere, like I have done above, so now when a web service times out, the orchestration will throw a timeout exception, this is much more intuitive than seeing a general exception!

SOAP exceptions from a web service

In order to catch soap exception from a web service, what you do is add a reference to the BizTalk project for:
System.Web.Services

Then add an exception handle block:


You can then access the information of the exception message like this:
e.Message
e.g.

Or you can create a Soap Exception message and send it to the message box for failure routing, here is an example from:
Source: Saravana Kumar, http://www.digitaldeposit.net/blog/2007/05/orchestration-handle-soap-exception-and.html

A word of warning, if a web service throws a soap exception, the port will retry by default 3 times at intervals of 5 minutes, I changed the retry here to 0 for my example, so that the soap exception is caught and a log in made in SQL, however if you using a delay shape make sure the amount in the delay is greater than the total interval in the send point, else you will never catch the soap exception and will just catch timeout exceptions!
So below, works fine with a delays of 2.5 minutes, however if I made retry count = 1, then the delay should be greater than 5 minutes! You can really get confused when a soap exception occurs and the orchestration does not catch it, it will only catch it after the retry count is done in the send port of the web service!

Here is the exception in SQL
Summary:
  1. Create a VARIABLE in the orchestration view of the exception type you want to catch, use mscorlib to assign the variable type to an exception
  2. Drag a throw shape and set the type of exception to throw to the variable name (yes it is automatically instantiated)
  3. Add a custom Catch block to the SCOPE where the throw shape is located and once again, set the properties of Exception Object Name and Exception Object Type, where type will come from mscorlib and name is anything you like!
Read more ...

Consuming Dynamics CRM 4.0 Web Services in BizTalk 2010

Tuesday, September 20, 2011

Post By Kent Weare

The project that I am on ran into a critical and somewhat embarrassing situation this past week.  As we moved from a single node project environment into a multi-node test environment we discovered that the Dynamics 4.0 Adapter cannot be installed on multiple BizTalk Servers in the same group.  Yes – you read that correctly.  I have never heard of this kind of limitation and struggle with the idea the adapter was actually made available without this feature.  You can read more about this limitation in the FAQ section here.
With this in mind, we needed to quickly make a 180 degree turn.  I had heard of other people calling the ASMX CRM Web Services from BizTalk via WCF Adapter.  The process of consuming these ASMX services is much like consuming any other Web Service.  That is until you start your application and get the dreaded 401 Unauthorized error. 

System.ServiceModel.Security.MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'. ---> System.Net.WebException: The remote server returned an error: (401) Unauthorized

This seems like a pretty common error within the BizTalk and CRM forums.  However, I was not able find any resolutions to the issue.  It wasn’t until I started stepping through a .NET SDK sample that I realized that I was missing something and that was a required SOAP header.  For your convenience I have built an end to end sample that will walk through creating a Lead within a Dynamics 4.0 system. Keep an eye open for the Message Assignment shape where I assign this header.
  • Once we have created a BizTalk project we need to “Add Generated Items” and then select “Consume WCF Service”
image
  • Even though Dynamics 4.0 still uses ASMX style Web Services we can select “Metadata Exchange (MEX) endpoint.
image
image
  • Once the WSDL definition has been loaded, click the “Next” button to continue.
  • Click “Import” to create your schemas and sample Orchestration.
  • You should find that the following artifacts have been added to your BizTalk Solution.
image
  • Our “main” schema is called CrmService_schemas_microsoft_com_crm_2007_WebServices.xsd.  Within this schema you will find all of the available Actions (Create/Update/Delete/Fetch etc) and the related entities (both custom and out of box).  In this scenario we are going to create a “Lead” within our CRM system.  So we can find the “Create” node and then scroll down and find the “Lead” entity.
image 
image
  • To keep things simple, I have created a little helper message that I will use in a Map to create an instance of this CRM Create message.
image
  • Inside my map, I will simply map from this helper message to the actual CRM Request.
image
  • With the building blocks in place, the next step was to update the  Orchestration that was generated for us.  Pretty standard stuff going on here.  We want to receive an instance of our “helper” message, transform it, send the request to CRM and then write the response to disk. 
image
  • When we generated our CRM schemas, BizTalk also created a Port Type called “CrmServiceSoap” that contains all of the available operations including “Create”.
image
  • Earlier in this post I mentioned that we need to set a SOAP header on the CRM Request message.  In order to do this we want to add a Message Assignment shape after our Transformation shape.
    image
  • Within this shape we want to set the following:
msgCreateCustomer (WCF.OutboundCustomHeaders) = "<headers><CrmAuthenticationToken xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\"><AuthenticationType xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">0</AuthenticationType><OrganizationName xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">YourOrganization</OrganizationName><CallerId xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">00000000-0000-0000-0000-000000000000</CallerId></CrmAuthenticationToken></headers>";
  • The two important settings are the “AuthenticationType” and the “OrganizationName” properties.    The Authentication property can take one of three values:
    • 0 – AD
    • 1 – Passport
    • 2 – Internet Facing Deployment
  • Since this is an on-premise installation we will go with 0.
  • The “OrganizationName” property becomes extremely important in multi-tenant configurations where you may have multiple organizations or departments sharing a CRM implementation.  Since we only have 1 organization in our deployment it is safe to just set our default value.
  • It is now time to deploy and configure the BizTalk application.
  • When BizTalk generated our CRM schemas and sample Orchestration, it also generated a Binding file.  When we import the binding file we will discover that two send ports have been created.  The first is a basicHttpBinding and the second send port uses a Custom Adapter with a Custom binding.  What is the difference?  Really it comes down to some addition security configuration that is available with the Custom adapter.
image
  • Using these binding as is didn’t work for me.  I needed to change the WCF-Custom Adapter to use the basicHttpBinding.  I also had to make a few Security related changes including setting the Security “mode” was set to “TransportCredentialOnly”
image
  • I also needed to set the “clientCredentialType” to “Windows”
image
  • I wasn’t quite done here.  I also need to add an Endpoint Behavior.  In this case it was a “clientCredentials” behavior.  Within this behavior, I set “Windows” - “allowedImpersonation” = “Impersonation”
image
  • At this point, I am ready to configure the rest of the application and start it.  Once started, I can process my sample helper message.
  • Within my sample file, I have provided the following information.  Once processed by BizTalk, I expect this Lead to be created within our CRM system and the CRM response to be send to disk.
image
  • The response from CRM is a GUID.
image
  • Once we launch the Dynamics CRM GUI, I am able to find my Barry Sanders record. 
image
Conclusion
If you have high-availability requirements then I highly recommend abandoning the Dynamics CRM 4.0 adapter.  Currently, and I am not sure if there are any plans, CRM 2011 does not have an adapter so now is as good as time as any to make the move.   You can read more about CRM 2011 integration on Richard Seroter’s blog.  Also look for a chapter on CRM 2011 integration in our upcoming book.
Read more ...

How to Throw Typed Fault Exceptions from Orchestrations Published as WCF Services

Tuesday, September 6, 2011

source : paulo salvatori's Blog

 

Introduction

In general, a WCF Web Service can return two types of SOAP faults: typed and untyped SOAP faults.

Typed Faults

In order to throw a typed fault, a service operation must be decorated with a System.ServiceModel.FaultContractAttribute that specifies a fault data contract defined earlier. The following code snippet shows a WCF web service called HelloWorld which implements the IHelloWorld service or contract interface. This interface exposes a single synchronous operation called SayHello that is decorated with the FaultContractAttribute. The attribute declares that the SayHello method can throw a typed fault exception defined by the CustomError data contract class. The implementation of the SayHello method in the HelloWorld class checks if the incoming request is null and in this case it throws an error of type FaultException<CustomError>.
namespace Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.Services
{
    [Serializable]
    [DataContract(Name = "HelloWorldRequest", Namespace = "http://microsoft.biztalk.cat/10/helloworld")]
    public class HelloWorldRequest
    {
       ...
    }

    [Serializable]
    [DataContract(Name = "HelloWorldResponse", Namespace = "http://microsoft.biztalk.cat/10/helloworld")]
    public class HelloWorldResponse
    {
        ...
    }

    [Serializable]
    [DataContract(Name = "CustomError", Namespace = "http://microsoft.biztalk.cat/10/customerror")]
    public class CustomError
    {
        ...
    }

    [ServiceContract(Name = "HelloWorld", Namespace = "http://microsoft.biztalk.cat/10/helloworld")]
    public interface IHelloWorld
    {
        [OperationContract(Action = "SayHello", ReplyAction = "SayHello", ProtectionLevel = ProtectionLevel.None)]
        [FaultContract(typeof(CustomError), Action = "CustomErrorFault")]
        HelloWorldResponse SayHello(HelloWorldRequest request);
    }

    [ServiceBehavior(Name = "HelloWorld", Namespace = "http://microsoft.biztalk.cat/10/helloworld")]
    public class HelloWorld : IHelloWorld
    {
        [OperationBehavior]
        public HelloWorldResponse SayHello(HelloWorldRequest request)
        {
            if (request == null || string.IsNullOrEmpty(request.Name))
            {
                throw CreateFault(NameCannotBeNullOrEmpty);
            }
            return new HelloWorldResponse(string.Format(SayHelloFormat, request.Name));
        } 

         private FaultException<CustomError> CreateFault(string message)
        {
             ...
            return fault;
        }
    }
}

 

Untyped Faults

Untyped SOAP faults are those that do not require a service operation to be decorated with a FaultContractAttribute that specify a custom data contract class.

BizTalk Orchestrations and Typed Faults

BizTalk Server 2006 R2 and BizTalk Server 2009 allow handling typed fault contracts when consuming WCF services from within orchestrations. The steps necessary to implement this technique are clearly described in the  article “How to Handle Typed Fault Contracts in Orchestrations” on MSDN. Instead, WCF adapters actually do not support processing typed fault contract exceptions within orchestrations published as WCF services. However, untyped SOAP faults can always be returned by orchestrations or pipelines. For more information on this topic review the article “How to Throw Fault Exceptions from Orchestrations Published as WCF Services” on MSDN.
This constraint arises from how the Receive Handler of WCF Adapters is actually implemented. The WCF Receive Adapter instantiates a singleton instance of theBizTalkServiceInstance class for each WCF Receive Location. This class can be found in the Microsoft.BizTalk.Adapter.Wcf.Runtime.dll assembly. In particular, as you can see in the picture below, the BizTalkServiceInstance class is decorated with the attribute ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple).
Reflector1

Therefore, all the incoming requests towards a given WCF Receive Location are are managed by the same singleton object. When you enable a WCF Receive Location, the Adapter initializes and opens a dedicated instance of a ServiceHost-derived class (WebServiceHost), which dynamically builds the WCF runtime components within the in-process or isolated host process. This includes the channel stack, dispatcher, and generic service instance. Almost all of the WCF adapters can be hosted within the BizTalk service process itself – the only exception is WCF-CustomIsolated, which must be used in a BizTalk isolated host by design. Even the HTTP adapters can be hosted in-process now. The WCF Adapters build the generic service contracts shown in the table below. Each service contract implemented by the BizTalkServiceInstance covers a different scenario.
[ServiceContract(Namespace = "http://www.microsoft.com/biztalk/2006/r2/wcf-adapter")]
public interface IOneWayAsync
{
    // Methods
    [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
    IAsyncResult BeginOneWayMethod(Message message, AsyncCallback callback, object state);
    [OperationContract(IsOneWay = true, Action = "BizTalkSubmit")]
    void BizTalkSubmit(Message message);
    void EndOneWayMethod(IAsyncResult result);
}

[ServiceContract(Namespace = "http://www.microsoft.com/biztalk/2006/r2/wcf-adapter")]
public interface IOneWayAsyncTxn
{
    // Methods
    [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
    IAsyncResult BeginOneWayMethod(Message message, AsyncCallback callback, object state);
    [OperationContract(IsOneWay = true, Action = "BizTalkSubmit")]
    void BizTalkSubmit(Message message);
    void EndOneWayMethod(IAsyncResult result);
}

[ServiceContract(Namespace = "http://www.microsoft.com/biztalk/2006/r2/wcf-adapter")]
public interface ITwoWayAsync
{
    // Methods
    [OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")]
    IAsyncResult BeginTwoWayMethod(Message message, AsyncCallback callback, object state);
    [OperationContract(IsOneWay = false, Action = "BizTalkSubmit")]
    Message BizTalkSubmit(Message message);
    Message EndTwoWayMethod(IAsyncResult result);
}

[ServiceContract(Namespace = "http://www.microsoft.com/biztalk/2006/r2/wcf-adapter")]
public interface ITwoWayAsyncVoid
{
    // Methods
    [OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")]
    IAsyncResult BeginTwoWayMethod(Message message, AsyncCallback callback, object state);
    [OperationContract(IsOneWay = false, Action = "BizTalkSubmit")]
    void BizTalkSubmit(Message message);
    void EndTwoWayMethod(IAsyncResult result);
}

[ServiceContract(Namespace = "http://www.microsoft.com/biztalk/2006/r2/wcf-adapter")]
public interface ITwoWayAsyncVoidTxn
{
    // Methods
    [TransactionFlow(TransactionFlowOption.Mandatory), OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")]
    IAsyncResult BeginTwoWayMethod(Message message, AsyncCallback callback, object state);
    [TransactionFlow(TransactionFlowOption.Mandatory), OperationContract(IsOneWay = false, Action = "BizTalkSubmit")]
    void BizTalkSubmit(Message message);
    void EndTwoWayMethod(IAsyncResult result);
}

As you can easily see in the code above, all the service contracts implemented by the BizTalkServiceInstance Class are generic and asynchronous. In fact, one-way service operations are decorated with [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")] attribute, while request-response methods are decorated with the[OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")] attribute. Besides, they receive a generic inbound message of typeSystem.ServiceModel.Channels.Message and eventually return a message of the same type. As a consequence, these methods can accept any request message and eventually process any reply message. However, since these operations are generic and untyped, they are not decorated by any FaultContractAttribute. As a consequence, WCF Receive Locations cannot return typed fault exceptions. Since every WCF Receive Location is implemented as an instance of the BizTalkServiceInstance and this class cannot be customized or inherited to expose typed service contracts, BizTalk Server does not natively support throwing typed fault exceptions within orchestrations published as WCF services or more in general throwing typed fault exceptions within a WCF Receive Location.
At this point a question arises: is there any workaround to throw typed fault exceptions within orchestrations published as WCF services? The answer, fortunately, is yes.

The Solution

The WCF-Custom and WCF-CustomIsolated Adapters provided by BizTalk Server 2006 R2 and BizTalk Server 2009 allow to define a custom WCF binding configuration to meet your precise communication needs. These adapters can be used to define custom WCF configurations for Send Ports or Receive Locations and extend their standard functionality using custom components as endpoint and service behaviors, message inspectors, custom bindings, binding elements, channels, etc. Therefore, I decided to create the following custom components:
  • Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.WsdlExportExtensions assembly: flattens and extends the WSDL generated by the BizTalk WCF Service Publishing Wizard to enable WCF Receive locations to expose typed soap faults.
  • Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.MessageInspector assembly: intercepts a reply message and when this latter is a fault, it creates and returns a typed fault message.
The following picture depicts the architecture of the proof of concept I implemented to test these components:
Diagram
  1. A WCF-CustomIsolated Request-Response Receive Location receives a new xml document from a client application.
  2. The XML disassembler component within the XMLTransmit pipeline promotes the MsgType context property. The Message Agent submits the incoming message to the MessageBox (BizTalkMsgBoxDb).
  3. The inbound request starts a new instance of the HelloWorld orchestration type.
  4. The orchestration checks the incoming HelloWorldRequest message, and if the Name element is null or empty, it creates and return a new fault message of typeCustomError. Otherwise, the orchestration returns a reply message of type HelloWorldResponse.
  5. The HelloWorld orchestration publishes the reply message to the MessageBox (BizTalkMsgBoxDb).
  6. The response message is retrieved by the WCF-CustomIsolated Request-Response Receive Location.
  7. The reply  message is processed by the CustomErrorMessageInspector component. If the response is a fault message, it creates a new typed fault message using the configuration data set on the Receive Location.
  8. The reply message is returned to the client application.
The following picture shows the XML Schema which defines the CustomError typed fault.
CustomErrorXsd

The following figure shows the configuration of the WCF-CustomIsolated Request-Response Receive Location.
RLConfig01RLConfig02
RLConfig03RLConfig04

The Receive Location exposes an endpoint that uses the WsHttpBinding and make use of the serviceThrottling service behavior (seeSystem.ServiceModel.Description.ServiceThrottlingBehavior for more information), a custom service behavior called errorHandler and a custom endpoint behavior calledwsdlExport :
The custom service and endpoint behaviors must be configured in the machine.config as follows:
Machine.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    ...
  <system.serviceModel>
      ...
    <extensions>
      <behaviorExtensions>
          ...
        <add name="errorHandler" type="Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.ErrorHandler.CustomErrorBehaviorExtensionElement, 
                                       Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.ErrorHandler, Version=1.0.0.0, Culture=neutral, 
                                       PublicKeyToken=d7f63d8d08d8f3a2" />
        <add name="wsdlExport" type="Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.WsdlExportExtensions.WsdlExportBehaviorExtensionElement,
                                     Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.WsdlExportExtensions, Version=1.0.0.0, Culture=neutral,
                                     PublicKeyToken=d7f63d8d08d8f3a2" />
      </behaviorExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

The Include exception detail in faults option must be set on the Messages tab to enable the WCF Receive Location to return the error detail in fault messages. Below you can see the code of the CustomErrorHandler component. The constructor retrieves configuration data from the Receive Port configuration, while theIErrorHandler.ProvideFault method allows, in case of error, to generate a typed fault exception using the configuration data.
CustomErrorHandler Class
namespace Microsoft.BizTalk.CAT.Samples.PublishTypedFaults.ErrorHandler
{
    /// <summary>
    /// This class can be customized to create a message inspector.
    /// </summary>
    public class CustomErrorHandler : IErrorHandler
    {
        #region Private Constants
        private const string Source = "CustomErrorHandler";
        private const string SoapActionFormat = "[CustomErrorHandler]: SOAP Fault Action = [{0}]";
        private const string SoapFaultCodeFormat = "[CustomErrorHandler]: SOAP Fault Code = [{0}]";
        private const string SoapFaultReasonFormat = "[CustomErrorHandler]: SOAP Fault Reason = [{0}]";
        private const string SoapFaultDetailFormat = "[CustomErrorHandler]: SOAP Fault Detail:";
        private const string MessageTypeFormat = "{0}#{1}";
        private const string DefaultAction = "CustomError";
        private const string DefaultFaultCode = "CustomError";
        private const string DefaultFaultReason = "CustomError";
        #endregion

        #region Private Fields
        private bool enabled = true;
        private bool traceEnabled = false;
        private int maxBufferSize = 2097152;
        private string typedFaults = null;
        private Dictionary<string, CustomErrorFaultInfo> faults = new Dictionary<string, CustomErrorFaultInfo>();
        #endregion

        #region Public Constructors
        public CustomErrorHandler(bool enabled,
                                  bool traceEnabled,
                                  int maxBufferSize,
                                  string typedFaults)
        {
            this.enabled = enabled;
            this.traceEnabled = traceEnabled;
            this.maxBufferSize = maxBufferSize;
            this.typedFaults = typedFaults;

            try
            {
                if (!string.IsNullOrEmpty(typedFaults))
                {
                    TypedFaults faultData = SerializationHelper.XmlDeserialize(typedFaults, typeof(TypedFaults)) as TypedFaults;
                    if (faultData != null &&
                        faultData.FaultList != null &&
                        faultData.FaultList.Count > 0)
                    {
                        for (int i = 0; i < faultData.FaultList.Count; i++)
                        {
                            if (!string.IsNullOrEmpty(faultData.FaultList[i].Name))
                            {
                                faults.Add(string.Format(MessageTypeFormat, faultData.FaultList[i].Namespace, faultData.FaultList[i].Name ?? string.Empty),
                                           new CustomErrorFaultInfo(faultData.FaultList[i].Action, 
                                                                    faultData.FaultList[i].Code, 
                                                                    faultData.FaultList[i].Reason));
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(Source, ex.Message, EventLogEntryType.Error);
            }
        }
        #endregion

        #region IErrorHandler Members

        public bool HandleError(Exception error)
        {
            return true;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            try
            {
                if (error != null)
                {
                    string action = null;
                    FaultCode faultCode = null;
                    FaultReason faultReason = null;
                    XPathNavigator details = null;

                    RetrieveErrorData(error.Message, out action, out faultCode, out faultReason);
                    XPathDocument document = new XPathDocument(new StringReader(error.Message));
                    details = document.CreateNavigator();
                    MessageFault messageFault = MessageFault.CreateFault(faultCode, faultReason, details, new CustomErrorSerializer());
                    fault = Message.CreateMessage(version, messageFault, action);
                }
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(Source, ex.Message, EventLogEntryType.Error);
                throw ex;
            }
            return;
        }
        #endregion

        #region Private Methods
        private void RetrieveErrorData(string message, 
                                       out string action, 
                                       out FaultCode faultCode,
                                       out FaultReason faultReason)
        {
            action = DefaultAction;
            faultCode = new FaultCode(DefaultFaultCode);
            faultReason = new FaultReason(DefaultFaultReason);

            if (string.IsNullOrEmpty(message))
            {
                return;
            }

            if (traceEnabled)
            {
                TraceHelper.WriteLine(SoapFaultDetailFormat);
                TraceHelper.WriteLine(message);
            }

            string rootElement = null;
            string targetNamespace = null;

            using (StringReader stringReader = new StringReader(message))
            {
                XmlReader xmlReader = null;
                try
                {
                    xmlReader = XmlReader.Create(stringReader);
                    bool ok = true;
                    while (xmlReader.Read() && ok)
                    {
                        if (xmlReader.NodeType == XmlNodeType.Element)
                        {
                            rootElement = xmlReader.LocalName;
                            targetNamespace = xmlReader.NamespaceURI;
                            ok = false;
                        }
                    }
                }
                catch (Exception)
                {
                }
                finally
                {
                    if (xmlReader != null)
                    {
                        xmlReader.Close();
                    }
                }
            }
            if (!string.IsNullOrEmpty(rootElement))
            {
                if (targetNamespace == null)
                {
                    targetNamespace = string.Empty;
                }
                string messageType = string.Format(MessageTypeFormat, targetNamespace, rootElement);
                if (faults != null &&
                    faults.ContainsKey(messageType))
                {
                    if (!string.IsNullOrEmpty(faults[messageType].Action))
                    {
                        action = faults[messageType].Action;
                    }
                    if (!string.IsNullOrEmpty(faults[messageType].Code))
                    {
                        faultCode = new FaultCode(faults[messageType].Code);
                    }
                    if (!string.IsNullOrEmpty(faults[messageType].Reason))
                    {
                        faultReason = new FaultReason(faults[messageType].Reason);
                    }
                }
                if (traceEnabled)
                {
                    TraceHelper.WriteLine(string.Format(SoapActionFormat, action));
                    TraceHelper.WriteLine(string.Format(SoapFaultCodeFormat, faultCode));
                    TraceHelper.WriteLine(string.Format(SoapFaultReasonFormat, faultReason));
                }
            }
        }
        #endregion
    }
}

The following table contains the XML snippet used as configuration data by the CustomErrorHandler component. As you can see, the convention I used allows defining multiple typed faults in a declarative way. At runtime the CustomErrorHandler component will read and store configuration data in a dictionary. In particular the value of the Name and Namespace elements of each TypedFault will be concatenated (Namespace#Name) to form the  message type of the corresponding error message. If the orchestration returns a typed fault, the CustomErrorHandler component will determine the message type of the typed fault (targetNamespace#RootElementName) and it will retrieve the corresponding information (Action, Code, and Reason) from the dictionary.
<TypedFaults xmlns="http://microsoft.biztalk.cat/10/typedfaults">
  <TypedFault>
    <Action>SayHello</Action>
    <Name>CustomError</Name>
    <Namespace>http://microsoft.biztalk.cat/10/customerror</Namespace>
    <Code>SayHello Error Code</Code>
    <Reason>SayHello Orchestration Error</Reason>
  </TypedFault>
</TypedFaults>

If you set the TraceEnabled property to true, at runtime the CustomErrorHandler component will produce a trace that you can intercept and review with a tool such asDebug as shown in the picture below.
DebugView
The WCF-Receive Location can be created using the BizTalk WCF Service Publishing Wizard. In particular,  this allows to create a WCF Receive Location to expose theHelloWorld orchestration as a WCF service. Moreover, setting the Enable metadata endpoint option it’s possible to enable the  WCF Receive Location to expose a Metadata Endpoint.
BizTalkWCFServicePublishingWizard

This allows a developer to use Visual Studio or a tool such as svcutil to generate a proxy class to invoke  the WCF Receive Location. Now, let’s say that you want to create a WCF client application to invoke the WCF Receive Location. Within Visual Studio you can create a new Windows Application, right-click the project inside the Solution Explorer and then select Add Service Reference. If the WCF Receive Location exposes a Metadata Endpoint, this operation will create a proxy class to invoke the corresponding WCF web service. However, if you review the code and in particular the contract interface, you’ll realize that the SayHello method is not decorated with aFaultContractAttribute. This is due to the fact, that since BizTalk Server does not support throwing typed fault exceptions, the native wsdl returned by the Metadata Endpoint exposed by the WCF Receive location does not contain any soap fault message. In order to sort out this problem, you can adopt one of the following techniques:
  • Create and expose a custom wsdl that contains the definition of your typed soap faults.
  • Create a custom endpoint behavior to dynamically  modify the wsdl produced by BizTalk.
The first technique is quite straightforward:
  • You manually define a custom wsdl using a text or xml editor.
  • You publish the resulting wsdl file to IIS (e.g. http://localhost/samples/service.wsdl)
  • You configure your WCF-Custom or WCF-CustomIsolated Receive Location to expose metadata and in particular the newly created wsdl file.
In order to accomplish the finale step, you can proceed as follows:
  • Start the BizTalk Server Administration Console.
  • Open your WCF-Custom/WCF-CustomIsolated Receive Location.
  • Click the Configure button in the Transport section.
  • Click the Behavior tab.
  • Right-click the Service Behavior node and choose to Add Extension.
  • In the Select Behavior Extension window, choose the serviceMetadata component.
  • Set the value of the httpGetEnabled property of the serviceMetadata behavior to True (see the picture below).
  • Set the value of the externalMetadataLocation property of the serviceMetadata behavior to the url of your hand-built wsdl (see the picture below).
serviceMetadata

The second technique consists in creating a custom component called WsdlExtensions to modify at run-time the wsdl returned by the the Metadata Endpoint exposed by a WCF-Custom/WCF-CustomIsolated Receive location. This component is implemented as an a custom endpoint behavior and it can be used along with any WCF-Customor WCF-CustomIsolated Receive Location, as shown in the picture below.
WsdlExportTab

The WsdlExtensions property exposed by the component accepts an XML snippet that allows to specify how the wsdl has to be customized at runtime.
<WsdlExtensions xmlns="http://microsoft.biztalk.cat/10/wsdlextensions">
    <Prefix>bts</Prefix>
    <XmlSchemas>
        <XmlSchema>
            <Name>CustomError</Name>
            <Namespace>http://microsoft.biztalk.cat/10/customerror</Namespace>
        </XmlSchema>
    </XmlSchemas>
    <Messages>
        <Message>
            <Name>HelloWorld_SayHello_CustomErrorFault_FaultMessage</Name>
            <Namespace>http://microsoft.biztalk.cat/10/customerror</Namespace>
            <Parts>
                <Part>
                    <Name>detail</Name>
                    <Element>CustomError</Element>
                </Part>
            </Parts>
        </Message>
    </Messages>
    <PortTypes>
        <PortType>
            <Name>HelloWorld</Name>
            <Operations>
                <Operation>
                    <Name>SayHello</Name>
                    <Faults>
                        <Fault>
                            <Name>CustomErrorFault</Name>
                            <Message>HelloWorld_SayHello_CustomErrorFault_FaultMessage</Message>
                        </Fault>
                    </Faults>
                </Operation>
            </Operations>
        </PortType>
    </PortTypes>
</WsdlExtensions>

As shown in the picture above, the components allows to define the following information:
  • The prefix for the namespace of new messages created inside the wsdl.
  • One or multiple Xml Schemas each defining a different typed fault. These schemas must be deployed to BizTalk. At runtime, the component is able to retrieve the content of each schema from the BizTalkMgmtDb that  subsequently is inserted in the outbound wsdl.
  • One or multiple fault messages, each containing one or multiple parts.
  • One or multiple operation-fault associations. At runtime the component search through the original wsdl structure and creates faults accordingly.
At runtime the WsdlExtensions component validates the XML configuration specified on the port and if the TraceEnabled property is set to true, it produces a trace with a tool such as Debug as shown in the picture below.
DebugViewAddServiceReference

The following picture shows the wsdl produced by the WsdlExtensions component at runtime. In particular, the parts added by the component are highlighted by a red frame.
WSDL
Note: Flattening the wsdl  to include the XML schemas used to define messages allows to make the wsdl document compatible with non-Microsoft development environments. In fact, Visual Studio supports import and include directives in a wsdl, but some non-Microsoft development environments are not able to consume the wsdl exposed by a WCF web service.
Note: the XML Schemas defining the structure of the XML configuration data consumed at runtime respectively by the CustomErrorMessageInspector and by theWsdlExportEndpointBehavior can be found inside the BehaviorSchemas project. The corresponding assembly doesn’t need to be deployed to BizTalk Server.

Proof Of Concept in Action

In order to test the WCF custom components, I created a WinForms application. In particular, when I leave the Name textbox blank and press the Call button, theHelloWorld orchestration returns a fault message. The client application intercepts the error with a catch (FaultException<HelloWorldBizTalk.CustomError> ex) block and opens a MessageBox to show the detail of the exception.
ClientApplication
I created 3 different Receive Locations to test my ErrorHandler component with different WCF settings:
  • PublishTypedFaults.HelloWorld.WCF-CustomIsolated.NoSecurity.ReceiveLocation
    • Adapter: WCF-CustomIsolated
    • Binding: WsHttpBinding
    • Security Mode: None
    • ClientCredentialType: N/A

  • PublishTypedFaults.HelloWorld.WCF-CustomIsolated.TransportSecurity.ReceiveLocation
    • Adapter: WCF-CustomIsolated
    • Binding: WsHttpBinding
    • Security Mode: Transport
    • ClientCredentialType: Windows

  • PublishTypedFaults.HelloWorld.WCF-Custom.MessageSecurity.ReceiveLocation
    • Adapter: WCF-Custom
    • Binding: NetTcpBinding
    • Security Mode: Message
    • ClientCredentialType: Windows

 

Show me the code!

At this point, you are probably saying:
- ok, cool, where’s the code?
Well, you can find the code for BizTalk Server 2009 here, but its should work fine also on BizTalk Server 2006 R2. Obviously, in this latter case you need to readapt the projects for Visual Studio 2005.
Obviously, everything there are many ways to implement the same functionalities, so I’d be glad if you could leave a message on my blog and let me know how you used/customized/improved my code. Enjoy! ;-)
Read more ...