Introduction

Microsoft BizTalk Server enables organizations to connect and extend heterogeneous systems across the enterprise and with trading partners. The Service Bus provides connectivity, queuing, and routing capabilities to both cloud and on-premises applications. The Service Bus is available in the cloud as part of the Windows Azure platform and on-premises with the name Service Bus for Windows Server as a set of installable components and services. This sample is the
evolution of my previous article Howto integrate a BizTalk Server 2010 application with Service Bus Queues and Topics and it demonstrates how to use WCF along with the NetMessagingBinding to integrate a BizTalk Server 2010/2013 application with the Service Bus for Windows Server.
The solution uses the following elements to realize the integration between the two products:
In this article, you will learn how to use these components in a BizTalk application and how to configure a WCF client application to perform the following operations:
In order to send messages to the BizTalk application, you can use the Service Bus Explorer tool or you can leverage the client application included in the solution. This client application makes use of the Microsoft.ServiceBus.dll to
exchange messages with the BizTalk application via WCF.

Scenario 1

The following picture shows the first of the two scenarios implemented by this sample. In this context, the Windows Forms client application simulates a line-of-business system running on-premises that exchanges messages with a BizTalk Server application by using queues, topics and subscriptions provided by the Service Bus for Windows Server messaging infrastructure. In this sample, the BizTalk Server 2010/2013 application uses always the static send ports and the same queue or topic to send the response back to the caller.

Message Flow

  1. The Windows Forms client application uses a WCF proxy to send a request message to the requestqueue or to the requesttopic.
  2. The BizTalk Server application uses a one-way receive port to receive request messages from the Service     Bus. In particular, a WCF-Custom receive location called ServiceBusForWindowsServer.WCF-Custom.NetMessagingBinding.Queue.ReceiveLocation is used to read request messages from the requestqueue, while another WCF-Custom receive location called ServiceBusForWindowsServer.WCF-Custom.NetMessagingBinding.Subscription.ReceiveLocation is used to receive request messages from the italyMilan subscription of the requesttopic. Both receive locations are configured to use the following     components:       
    • The NetMessagingBinding is used to receive messages from a queue or subscription.
    • A custom WCF Message Inspector called ServiceBusMessageInspector. At runtime, this component reads the BrokeredMessageProperty from the Properties collection of the inbound WCF Message and transforms its standard properties (such as MessageId and ReplyTo) and application specific properties (the key/value pairs contained in the Properties collection of the BrokeredMessageProperty) into context properties of the BizTalk message.
    • The ListenUriEndpointBehavior is used by the WCF-Custom receive location that retrieves messages from the ItalyMilan subscription defined on the requesttopic. This custom component is used to set the value of the ListenUri property of the service endpoint. See later in this article for more       information about this component. 
    • If an application retrieves messages from a sessionful queue or subscription, the SessionChannelEndpointBehavior must be added to the configuration of the WCF-Custom receive location. This custom component is used to make sure that at runtime the WCF-Custom adapter creates an IInputSessionChannel in place of an IInputChannel. This is a mandatory requirement to receive messages from a sessionful messaging entity.
    • The TokenProviderEndpointBehavior allows the receive location to authenticate against the local STS used by the Service Bus namespace by using the WindowsTokenProvider or OAuthTokenProvider. When using the WindowsTokenProvider, you can use the service account of the host process running the receive location to authenticate against the local STS or you can specify alternative credentials. When using the  OAuthTokenProvider you always need to specify credentials in the form of username, password or domain where the latter can be the name of the machine for local users or the name of the domain when using a domain account to access the Service Bus namespace. In any case, you need to make sure that user account used by the receive location is authorized to access the Service Bus namespace hosting the queue or subscription. In order to authorize the user account to access the namespace you can use the New-SBNamespace PowerShell cmdlet when you create the namespace or the Set-SBNamespace cmdlet to add the user account to the group of the namespace managers.
  3. The Message Agent submits the request message to the MessageBox (BizTalkMsgBoxDb).
  4. The inbound request starts a new instance of the StaticSendPortOrchestration. The latter uses a Direct Port to receive request messages that satisfy following filter expression: Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Schemas.Method
         == Static
    . The orchestration invokes a method exposed by the RequestManager helper component to calculate the response message. Then it copies the context properties from the request message to the response message and then assigns the value of the MessageId context property of the request message to the CorrelationId property of the response message. Finally, it checks the string value contained in the ReplyTo context property: if it contains the word "queue," the orchestration returns the response message through a logical port bound to a WCF-Custom send port configured to send messages to the responsequeue, otherwise it publishes the response to the MessageBox via a logical send port bound to a WCF-Custom send port configured to send messages to the responsetopic.
  5. The Message Agent submits the request message to the MessageBox (BizTalkMsgBoxDb).
  6. The response message is consumed by one of the following WCF-Custom send ports:       
    • ServiceBusForWindowsServer.WCF-Custom.NetMessagingBinding.Queue.SendPort: this send port writes the response message to the responsequeue.
    • ServiceBusForWindowsServer.WCF-Custom.NetMessagingBinding.Topic.SendPort: this send port writes the response message to the responsetopic.
  7. The selected send port writes the response message to the responsequeue or responsetopic. Both send ports are configured to use the following components:          
    • The NetMessagingBinding is used to send messages to the Service Bus.
    • The ServiceBusMessageInspector transforms the message context properties into BrokeredMessage properties.
    • The TokenProviderEndpointBehavior allows the send port to authenticate against the local STS used by the Service Bus namespace by using the WindowsTokenProvider or OAuthTokenProvider. The same considerations on how to use of this component in a receive location apply to send ports.
  8. The client application uses a WCF service with two endpoints to retrieve the reply message from the     responsequeue or responsetopic. In an environment with multiple client applications, each of them should use a separate queue or subscription to receive response messages from BizTalk Server.
 

Scenario 2

The following picture shows the second scenario implemented by the solution. The Windows Forms client application still simulates a line-of-business that exchanges messages with a BizTalk Server 2010/2013 application by using queues, topics and subscriptions provided by the Service Bus fo Windows Server messaging infrastructure. However, in this case the client application specifies the URL of the response queue or topic in the ReplyTo property. This allows different client applications to send requests through the same queue or topic, but to receive responses via different queues and topics. Besides, this architecturea allows the BizTalk application to use a single dynamic send port to transmit responses to client applications.

Message Flow

  1. The Windows Forms client application uses a WCF proxy to send a request message to the requestqueue or to the requesttopic.
  2. The BizTalk Server application uses a one-way receive port to receive request messages from the Service     Bus. In particular, a WCF-Custom receive location called ServiceBusForWindowsServer.WCF-Custom.NetMessagingBinding.Queue.ReceiveLocation is used to read request messages from the requestqueue, while another WCF-Custom receive location called ServiceBusForWindowsServer.WCF-Custom.NetMessagingBinding.Subscription.ReceiveLocation is used to receive request messages from the italyMilan subscription of the requesttopic. Both receive locations are configured to use the following     components:       
    • The NetMessagingBinding is used to receive messages from a queue or subscription.
    • A custom WCF Message Inspector called ServiceBusMessageInspector. At runtime, this component reads the BrokeredMessageProperty from the Properties collection of the inbound WCF Message and transforms its standard properties (such as MessageId and ReplyTo) and application specific properties (the key/value pairs contained in the Properties collection of the BrokeredMessageProperty) into context properties of the BizTalk message.
    • The ListenUriEndpointBehavior is used by the WCF-Custom receive location that retrieves messages from the ItalyMilan subscription defined on the requesttopic. This custom component is used to set the value of the ListenUri property of the service endpoint. See later in this article for more       information about this component. 
    • If an application retrieves messages from a sessionful queue or subscription, the SessionChannelEndpointBehavior must be added to the configuration of the WCF-Custom receive location. This custom component is used to make sure that at runtime the WCF-Custom adapter creates an IInputSessionChannel in place of an IInputChannel. This is a mandatory requirement to receive messages from a sessionful messaging entity.
    • The TokenProviderEndpointBehavior allows the receive location to authenticate against the local STS used by the Service Bus namespace by using the WindowsTokenProvider or OAuthTokenProvider. When using the WindowsTokenProvider, you can use the service account of the host process running the receive location to authenticate against the local STS or you can specify alternative credentials. When using the  OAuthTokenProvider you always need to specify credentials in the form of username, password or domain where the latter can be the name of the machine for local users or the name of the domain when using a domain account to access the Service Bus namespace. In any case, you need to make sure that user account used by the receive location is authorized to access the Service Bus namespace hosting the queue or subscription. In order to authorize the user account to access the namespace you can use the New-SBNamespace PowerShell cmdlet when you create the namespace or the Set-SBNamespace cmdlet to add the user account to the group of the namespace managers.
  3. The Message Agent submits the request message to the MessageBox (BizTalkMsgBoxDb).
  4. The inbound request starts a new instance of the DynamicSendPortOrchestration. The latter uses a Direct Port to receive request messages that satisfy following filter expression: Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Schemas.Method
         == Dynamic
    . The orchestration invokes a method exposed by the RequestManager helper component to calculate the response message. Then it copies the context properties from the request message to the response message and then assigns the value of the MessageId context property of the request message to the CorrelationId property of the response message. Next, it reads the address of the messaging entity (queue or topic) to which to send the response from the ReplyTo context property. Finally, the orchestration configures the context properties of the response message and the properties of the dynamic send port to use the following components:    
    • The NetMessagingBinding is used to send messages to the Service Bus.
    • The ServiceBusMessageInspector transforms the message context properties into BrokeredMessage properties
    • The TokenProviderEndpointBehavior allows the receive location to authenticate against the local STS used by the Service Bus namespace by using the WindowsTokenProvider or OAuthTokenProvider.
  5. The Message Agent submits the request message to the MessageBox (BizTalkMsgBoxDb).
  6. The response message is consumed by the dynamic one-way send port named ServiceBusForWindowsServer.Dynamic.SendPort. 
  7. The dynamic send port uses the WCF-Custom adapter and NetMessagingBinding as specified by the orchestration to send back the response to the messaging entity (in my sample responsequeue or responsetopic) whose address has been specified by the client application in the ReplyTo property of the BrokeredMessageProperty.
  8. The client application uses a WCF service with two endpoints to retrieve the reply message from the     responsequeue or responsetopic. In an environment with multiple client applications, each of them should use a separate queue or subscription to receive response messages from BizTalk Server.
 

Environment Setup

 
The first operation to perform to properly configure the environment is creating the messaging entities used by the demoin a new or existing Service Bus namespace. The New-SBNamespace cmdlet can be used to create a new Service Bus for Windows Server service namespace entry in the Service Bus for Windows Server farm. Make sure to specify the service accounts used by the BizTalk Server host processes in the group of the service namespace managers as shown in the following PowerShell script:
 
 
PowerShell
Edit|Remove
# Import Windows Azure PowerShell Module 
Import-Module  "C:\Program Files\Service Bus\1.0\ServiceBus\ServiceBus.psd1" -ErrorAction Stop 
 
# Create a new service bus namespace 
New-SBNamespace -Name MyNamespace  ` 
-ManageUsers SendHostUserAccount@MyDomain.com, ReceiveHostUserAccount@MyDomain.com
 
The solution is configured to use the ServiceBusDefaultNamespace and the default Management Port (9355) that is created during the configuration of the Service Bus for Windows Server. If you want to use a different namespace or change the Management Port, you will have to perform the following actions:
In order to create the queues, topics and subscriptions required by the demo you can use of the Service Bus Explorer to import the entity definition from the ServiceBusDefaultNamespace_Entities.xml file that you can find in the Setup folder, as shown in the picture below.
For your convenience, I created a console application called Provisioning that uses the functionality provided by the NamespaceManager class to create the queues, topics, and subscriptions required by the solution. When it starts, the console applications prompt for information necessary to connect to the namespace such as the name, management port and runtime port of the namespace. Make sure to run the console application with a user account that is part of the managers group of the namespace in question. Next, the console application prompts for the value to assign to the properties of the entities to create such as EnabledBatchedOperations and EnableDeadLetteringOnMessageExpiration for queues. The Provisioning application creates the following entities in the specified Service Bus namespace:
The following figure displays the output of the Provisioning console application.
Whatever mechanism you use to adopt, the following pictures shows the entities used by the demo at the end of the provisioning process:

Data and Message Contracts 

I started defining the data and message contracts for the request and response messages. These two types of contracts have different roles in WCF:
  1. Data contracts provide a mechanism to map .NET CLR types that are defined in code and XML Schemas (XSD) defined by the W3C organization (www.w3c.org). Data contracts are published in the service’s metadata, allowing clients to convert the neutral, technology-agnostic representation of the data types to their native representations.
  2. Message contracts describe the structure of SOAP messages sent to and from a service and enable you to inspect and control most of the details in the SOAP header and body. Whereas data contracts enable interoperability through the XML Schema Definition (XSD) standard, message contracts enable you to interoperate with any system that communicates through SOAP. Using message contracts gives you complete control over the SOAP message sent to and from a service by providing access to the SOAP headers and bodies directly. This allows the use of simple or complex types to define the exact content of the SOAP parts.
In my solution, I created two separate projects called DataContracts and MessageContracts respectively. Indeed, I could have used just data contracts to model messages as message contracts add a degree of complexity. However, by assigning false to the IsWrapped property exposed by the MessageContractAttribute, you specify that the message body won’t be contained in a wrapper element. Typically, the wrapper element of a request message is the name of the operation invoked and it’s defined in the WSDL. Setting the value of the IsWrapped property to false, you can simply select the Body option in both the Inbound BizTalk message body and Outbound WCF message body sections on the Messages tab when configuring a WCF receive location. Otherwise you should define a Path in the Inbound BizTalk message body section to extract the payload from the inbound message, and specify a template in the Outbound WCF message body section to include the outgoing response message within a wrapper element.

Service Contracts

The next step was to define the ServiceContracts used by the client application to exchange messages with the Service Bus. To this purpose, I created a new project in my solution called ServiceContracts, and I defined two service contract interfaces used by the client application respectively to send and receive messages from the Service Bus messaging entities. Indeed, I created two different versions of the service contract used to receive response messages:
Note that the methods defined by all the contracts must be one-way.
C#
Edit|Remove
#region Using Directives 
using System.ServiceModelusing Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.MessageContracts; 
#endregion 
 
namespace Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.ServiceContracts 
{ 
    [ServiceContract(Namespace = "http://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver",  
                     SessionMode = SessionMode.Allowed)] 
    public interface ICalculatorRequest 
    { 
        [OperationContract(Action = "SendRequest", IsOneWay = true)] 
        [ReceiveContextEnabled(ManualControl = true)] 
        void SendRequest(CalculatorRequestMessage calculatorRequestMessage); 
    } 
 
    [ServiceContract(Namespace = "http://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver",  
                     SessionMode = SessionMode.Allowed)] 
    public interface ICalculatorResponse 
    { 
        [OperationContract(Action = "ReceiveResponse", IsOneWay = true)] 
        [ReceiveContextEnabled(ManualControl = true)] 
        void ReceiveResponse(CalculatorResponseMessage calculatorResponseMessage); 
    } 
 
    [ServiceContract(Namespace = "http://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver",  
                     SessionMode = SessionMode.Required)] 
    public interface ICalculatorResponseSessionful : ICalculatorResponse 
    { 
    } 
}

Client Application

To submit messages to the BizTalk Server application you can use the Service Bus Explorer as explained later on in the article, or you can use the client application provided as part of the solution. Since the Windows Forms application exchanges messages with the BizTalk application asynchronously through Service Bus messaging entities, it acts as a client and a service application at the same time. Windows Forms uses WCF and the NetMessagingBinding to perform the following actions:
  1. Send request messages to the requestqueue.
  2. Send request messages to the requesttopic.
  3. Receive response messages from the responsequeue.
  4. Receive response messages from the ItalyMilan subscription of the responsetopic.
Let’s start reviewing the configuration file of the client application that plays a central role in the definition of the WCF client and service endpoints used to communicate with the Service Bus.
 
XML
Edit|Remove
<?xml version="1.0"?> 
<configuration  <system.diagnostics    <sources      <source name="System.ServiceModel.MessageLogging"  
              switchValue="Warning, ActivityTracing"        <listeners          <add type="System.Diagnostics.DefaultTraceListener"  
               name="Default"            <filter type=""/> 
          </add> 
          <add name="ServiceModelMessageLoggingListener"            <filter type=""/> 
          </add> 
        </listeners> 
      </source> 
    </sources> 
    <sharedListeners      <add initializeData="C:\Client.svclog"  
           type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"  
           name="ServiceModelMessageLoggingListener"  
           traceOutputOptions="Timestamp"        <filter type=""/> 
      </add> 
    </sharedListeners> 
    <trace autoflush="true" indentsize="4"      <listeners        <clear/> 
        <add name="LogTraceListener"  
             type="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Client.LogTraceListener, Client"  
             initializeData=""/> 
      </listeners> 
    </trace> 
  </system.diagnostics> 
  <startup    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> 
  </startup> 
  <system.serviceModel    <diagnostics      <messageLogging logEntireMessage="true"  
                      logMalformedMessages="false"  
                      logMessagesAtServiceLevel="true"  
                      logMessagesAtTransportLevel="false"/> 
    </diagnostics> 
    <behaviors      <endpointBehaviors        <behavior name="securityBehavior"          <transportClientEndpointBehavior            <tokenProvider              <windowsAuthentication                <stsUris                  <stsUri value="https://uppy.europe.corp.microsoft.com:9355/" /> 
                </stsUris> 
              </windowsAuthentication> 
            </tokenProvider> 
          </transportClientEndpointBehavior> 
        </behavior> 
      </endpointBehaviors> 
    </behaviors> 
    <bindings      <netMessagingBinding        <binding name="netMessagingBinding"  
                 sendTimeout="00:03:00"  
                 receiveTimeout="00:03:00"  
                 openTimeout="00:03:00"  
                 closeTimeout="00:03:00"  
                 sessionIdleTimeout="00:01:00"  
                 prefetchCount="-1"          <transportSettings batchFlushInterval="00:00:01"/> 
        </binding> 
      </netMessagingBinding> 
    </bindings> 
    <client      <!-- Invoke BizTalk via Service Bus Queue --> 
      <endpoint address="sb://uppy.europe.corp.microsoft.com/ServiceBusDefaultNamespace/biztalk/requestqueue"  
                behaviorConfiguration="securityBehavior"  
                binding="netMessagingBinding"  
                bindingConfiguration="netMessagingBinding"  
                contract="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.ServiceContracts.ICalculatorRequest"  
                name="requestQueueClientEndpoint"/> 
      <!-- Invoke BizTalk via Service Bus Topic --> 
      <endpoint address="sb://uppy.europe.corp.microsoft.com/ServiceBusDefaultNamespace/biztalk/requesttopic"  
                behaviorConfiguration="securityBehavior"  
                binding="netMessagingBinding"  
                bindingConfiguration="netMessagingBinding"  
                contract="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.ServiceContracts.ICalculatorRequest"  
                name="requestTopicClientEndpoint"/> 
    </client> 
    <services      <service name="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Service.ResponseHandlerService"        <endpoint address="sb://uppy.europe.corp.microsoft.com/ServiceBusDefaultNamespace/biztalk/responsequeue"  
                  behaviorConfiguration="securityBehavior"  
                  binding="netMessagingBinding"  
                  bindingConfiguration="netMessagingBinding"  
                  name="responseQueueServiceEndpoint"  
                  contract="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.ServiceContracts.ICalculatorResponse"/> 
        <endpoint address="sb://uppy.europe.corp.microsoft.com/ServiceBusDefaultNamespace/biztalk/responsetopic"  
                  listenUri="sb://uppy.europe.corp.microsoft.com/ServiceBusDefaultNamespace/biztalk/responsetopic/Subscriptions/ItalyMilan"  
                  behaviorConfiguration="securityBehavior"  
                  binding="netMessagingBinding"  
                  bindingConfiguration="netMessagingBinding"  
                  name="responseSubscriptionServiceEndpoint"  
                  contract="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.ServiceContracts.ICalculatorResponse"/> 
      </service> 
    </services> 
    <extensions      <behaviorExtensions        <add name="connectionStatusBehavior" type="Microsoft.ServiceBus.Configuration.ConnectionStatusElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="tokenProviderEndpointBehavior" type="Microsoft.WindowsAzure.CAT.ServiceBusForWindowsServer.Behaviors.TokenProviderBehaviorExtensionElement, Microsoft.WindowsAzure.CAT.ServiceBusForWindowsServer.Behaviors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=197ec3eb961f773c"/> 
      </behaviorExtensions> 
      <bindingElementExtensions        <add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
      </bindingElementExtensions> 
      <bindingExtensions        <add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        <add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
      </bindingExtensions> 
    </extensions> 
  </system.serviceModel> 
</configuration> 
 Please find below a brief description of the main elements and sections of the configuration file:
Note: When you configure a WCF service endpoint to consume messages from a sessionful queue or subscription, the service contract needs to support sessions. Therefore, in our sample, when you configure the responseQueueServiceEndpoint or responseSubscriptionServiceEndpoint endpoints to receive, respectively, from a sessionful queue and subscription, you have to replace the ICalculatorResponse service contract with the sessionful ICalculatorResponseSessionful contract interface.
The following example shows the code used by the client application to start the ResponseHandlerService used to read response messages from the responsequeue and the ItalyMilan subscription of the responsetopic. We’ll examine the code of the service in the next section.
C#
Edit|Remove
private void StartServiceHost() 
{ 
    try 
    { 
        // Creating the service host object as defined in config 
        var serviceHost = new ServiceHost(typeof(ResponseHandlerService)); 
                 
        // Add ErrorServiceBehavior for handling errors encounter by servicehost during execution. 
        serviceHost.Description.Behaviors.Add(new ErrorServiceBehavior()); 
 
 
        foreach (var serviceEndpoint in serviceHost.Description.Endpoints) 
        { 
            if (serviceEndpoint.Name == "responseQueueServiceEndpoint") 
            { 
                responseQueueUri = serviceEndpoint.Address.Uri.AbsoluteUri; 
                WriteToLog(string.Format(ServiceHostListeningOnQueue, 
                                        serviceEndpoint.Address.Uri.AbsoluteUri)); 
            } 
            if (serviceEndpoint.Name == "responseSubscriptionServiceEndpoint") 
            { 
                responseTopicUri = serviceEndpoint.Address.Uri.AbsoluteUri; 
                WriteToLog(string.Format(ServiceHostListeningOnSubscription, 
                                            serviceEndpoint.ListenUri.AbsoluteUri)); 
            } 
        } 
 
        // Start the service host 
        serviceHost.Open(); 
        WriteToLog(ServiceHostSuccessfullyOpened); 
    } 
    catch (Exception ex) 
    { 
        mainForm.HandleException(ex); 
    } 
}
 The following picture shows the user interface of the client application.
The radio buttons contained in the Request Method group allow you to choose whether to send the request message to the requestqueue or the requesttopic, whereas the radio buttons contained in the Response Method group allows you to select whether to receive the response from the responsequeue or from the ItalyMilan subscription of the responsetopic. To communicate the selection to the underlying BizTalk application, the application uses a BrokeredMessageProperty object to assign the value of the responseQueueUri or responseTopicUri private fields to the ReplyTo property. The following table contains the code of the method used by the client application. For your convenience, comments have been added to the code to facilitate its understanding.
C#
Edit|Remove
private void SendRequestMessageUsingWCF(string endpointConfigurationName) 
{ 
    try 
    { 
        if (string.IsNullOrEmpty(endpointConfigurationName)) 
        { 
            WriteToLog(EndpointConfigurationNameCannotBeNull); 
            return; 
        } 
 
        // Set the wait cursor 
        Cursor = Cursors.WaitCursor; 
 
        // Make sure that the request message contains at least an operation 
        if (operationList == null || 
            operationList.Count == 0) 
        { 
            WriteToLog(OperationListCannotBeNull); 
            return; 
        } 
 
        // Create warning collection 
        var warningCollection = new List<string>(); 
 
        // Create request message 
        var calculatorRequest = new CalculatorRequest(operationList); 
        var calculatorRequestMessage = new CalculatorRequestMessage(calculatorRequest); 
 
        // Create the channel factory for the currennt client endpoint 
        // and cache it in the channelFactoryDictionary 
        if (!channelFactoryDictionary.ContainsKey(endpointConfigurationName)) 
        { 
            channelFactoryDictionary[endpointConfigurationName] = new ChannelFactory<ICalculatorRequest>(endpointConfigurationName); 
        } 
 
        // Create the channel for the currennt client endpoint 
        // and cache it in the channelDictionary 
        if (!channelDictionary.ContainsKey(endpointConfigurationName)) 
        { 
            channelDictionary[endpointConfigurationName] =  
                channelFactoryDictionary[endpointConfigurationName].CreateChannel(); 
        } 
 
        // Use the OperationContextScope to create a block within which to access the current OperationScope 
        using (new OperationContextScope((IContextChannel)channelDictionary[endpointConfigurationName])) 
        { 
            // Create a new BrokeredMessageProperty object 
            var brokeredMessageProperty = new BrokeredMessageProperty(); 
 
            // Read the user defined properties and add them to the   
            // Properties collection of the BrokeredMessageProperty object 
            foreach (var e in propertiesBindingSource.Cast<PropertyInfo>()) 
            { 
                try 
                { 
                    e.Key = e.Key.Trim(); 
                    if (e.Type != StringType && e.Value == null) 
                    { 
                        warningCollection.Add(string.Format(CultureInfo.CurrentUICulture,  
                                                            PropertyValueCannotBeNull, e.Key)); 
                    } 
                    else 
                    { 
                        if (brokeredMessageProperty.Properties.ContainsKey(e.Key)) 
                        { 
                            brokeredMessageProperty.Properties[e.Key] =  
                                ConversionHelper.MapStringTypeToCLRType(e.Type, e.Value); 
                        } 
                        else 
                        { 
                            brokeredMessageProperty.Properties.Add(e.Key,  
                                ConversionHelper.MapStringTypeToCLRType(e.Type, e.Value)); 
                        } 
                    } 
                } 
                catch (Exception ex) 
                { 
                    warningCollection.Add(string.Format(CultureInfo.CurrentUICulture,  
                        PropertyConversionError, e.Key, ex.Message)); 
                } 
            } 
 
            // if the warning collection contains at least one or more items, 
            // write them to the log and return immediately 
            StringBuilder builder; 
            if (warningCollection.Count > 0) 
            { 
                builder = new StringBuilder(WarningHeader); 
                var warnings = warningCollection.ToArray<string>(); 
                for (var i = 0; i < warningCollection.Count; i++) 
                { 
                    builder.AppendFormat(WarningFormat, warnings[i]); 
                } 
                mainForm.WriteToLog(builder.ToString()); 
                return; 
            } 
 
            // Set the BrokeredMessageProperty properties 
            brokeredMessageProperty.Label = txtLabel.Text; 
            brokeredMessageProperty.MessageId = Guid.NewGuid().ToString(); 
            brokeredMessageProperty.SessionId = sessionId; 
            brokeredMessageProperty.ReplyToSessionId = sessionId; 
            brokeredMessageProperty.ReplyTo = responseQueueRadioButton.Checked 
                                                ? responseQueueUri 
                                                : responseTopicUri; 
            OperationContext.Current.OutgoingMessageProperties.Add(BrokeredMessageProperty.Name,  
                                                                    brokeredMessageProperty); 
                     
            // Send the request message to the requestqueue or requesttopic 
            var stopwatch = new Stopwatch(); 
            try 
            { 
                stopwatch.Start(); 
                channelDictionary[endpointConfigurationName].SendRequest(calculatorRequestMessage); 
            } 
            catch (CommunicationException ex) 
            { 
                if (channelFactoryDictionary[endpointConfigurationName] != null) 
                { 
                    channelFactoryDictionary[endpointConfigurationName].Abort(); 
                    channelFactoryDictionary.Remove(endpointConfigurationName); 
                    channelDictionary.Remove(endpointConfigurationName); 
                } 
                HandleException(ex); 
            } 
            catch (Exception ex) 
            { 
                if (channelFactoryDictionary[endpointConfigurationName] != null) 
                { 
                    channelFactoryDictionary[endpointConfigurationName].Abort(); 
                    channelFactoryDictionary.Remove(endpointConfigurationName); 
                    channelDictionary.Remove(endpointConfigurationName); 
                } 
                HandleException(ex); 
            } 
            finally 
            { 
                stopwatch.Stop(); 
            } 
            // Log the request message and its properties 
            builder = new StringBuilder(); 
            builder.AppendLine(string.Format(CultureInfo.CurrentCulture, 
                    MessageSuccessfullySent, 
                    channelFactoryDictionary[endpointConfigurationName].Endpoint.Address.Uri.AbsoluteUri, 
                    brokeredMessageProperty.MessageId, 
                    brokeredMessageProperty.SessionId, 
                    brokeredMessageProperty.Label, 
                    stopwatch.ElapsedMilliseconds)); 
            builder.AppendLine(PayloadFormat); 
            for (var i = 0; i < calculatorRequest.Operations.Count; i++) 
            { 
                builder.AppendLine(string.Format(RequestFormat, 
                                                    i + 1, 
                                                    calculatorRequest.Operations[i].Operand1, 
                                                    calculatorRequest.Operations[i].Operator, 
                                                    calculatorRequest.Operations[i].Operand2)); 
            } 
            builder.AppendLine(SentMessagePropertiesHeader); 
            foreach (var p in brokeredMessageProperty.Properties) 
            { 
                builder.AppendLine(string.Format(MessagePropertyFormat, 
                                                    p.Key, 
                                                    p.Value)); 
            } 
            var traceMessage = builder.ToString(); 
            WriteToLog(traceMessage.Substring(0, traceMessage.Length - 1)); 
        } 
    } 
    catch (Exception ex) 
    { 
        // Handle the exception 
        HandleException(ex); 
    } 
    finally 
    { 
        // Restoire the defaulf cursor 
        Cursor = Cursors.Default; 
    } 
}

Response Handler Service 

The following table contains the code of the WCF service used by the client application to retrieve and log response messages from the responsequeue and ItalyMilan subscription of the responsetopic. To perform this operation, the service exposes two different endpoints each of which uses the NetMessagingBinding and receives messages from one of the two queues. Indeed, each subscription can be seen as a virtual queue getting copies of messages published to the topic they belong to. The example below shows the code of the ResponseHandlerService class. As you can notice, the service retrieves the BrokeredMessageProperty from the Properties collection of the incoming WCF message and uses this object to access to the properties of the response message. Since in the ICalculatorResponse service contract the ReceiveResponse method is decorated with the [ReceiveContextEnabled(ManualControl = true)] attribute, the service method must explicitly signal the receive. This signaling requires the service to explicitly invoke the ReceiveContext.Complete method to commit the receive operation. In fact, as we said at the beginning of the article, when the ManualControl property is set to true, the message received from the channel is delivered to the service operation with a lock for the message. The service implementation must call either Complete(TimeSpan) or Abandon(TimeSpan) to signal the receive completion of the message. Failure to call either of these methods causes the lock to be held on the message until the lock timeout interval elapses. Once the lock is released (either through a call to Abandon(TimeSpan)) or lock timeout) the message is redispatched from the channel to the service. Calling Complete(TimeSpan) marks the message as successfully received.
 
 
C#
Edit|Remove
[ServiceBehavior(Namespace = "http://windowsazure.cat.microsoft.com/samples/servicebus")] 
public class ResponseHandlerService : ICalculatorResponseSessionful 
{ 
    #region Private Constants 
    //*************************** 
    // Formats 
    //*************************** 
    private const string MessageSuccessfullyReceived = "Response Message Received:\n - EndpointUrl:[{0}]" + 
                                                        "\n - CorrelationId=[{1}]\n - SessionId=[{2}]\n - Label=[{3}]"; 
    private const string ReceivedMessagePropertiesHeader = "Properties:"; 
    private const string PayloadFormat = "Payload:"; 
    private const string StatusFormat = " - Status=[{0}]"; 
    private const string ResultFormat = " - Result[{0}]: Value=[{1}] Error=[{2}]"; 
    private const string MessagePropertyFormat = " - Key=[{0}] Value=[{1}]"; 
 
    //*************************** 
    // Constants 
    //*************************** 
    private const string Empty = "EMPTY"; 
    #endregion 
 
    #region Public Operations 
    [OperationBehavior] 
    public void ReceiveResponse(CalculatorResponseMessage calculatorResponseMessage) 
    { 
        try 
        { 
            if (calculatorResponseMessage != null && 
                calculatorResponseMessage.CalculatorResponse != null) 
            { 
                // Initialize calculatorResponse var 
                var calculatorResponse = calculatorResponseMessage.CalculatorResponse; 
 
                // Get the message properties 
                var incomingProperties = OperationContext.Current.IncomingMessageProperties; 
                var brokeredMessageProperty = incomingProperties[BrokeredMessageProperty.Name] as  
                                              BrokeredMessageProperty; 
 
                // Trace the response message 
                var builder = new StringBuilder(); 
                if (brokeredMessageProperty != null) 
                    builder.AppendLine(string.Format(MessageSuccessfullyReceived,  
                                                     OperationContext.Current.Channel.LocalAddress.Uri.AbsoluteUri, 
                                                     brokeredMessageProperty.CorrelationId ?? Empty, 
                                                     brokeredMessageProperty.SessionId ?? Empty, 
                                                     brokeredMessageProperty.Label ?? Empty)); 
                builder.AppendLine(PayloadFormat); 
                builder.AppendLine(string.Format(StatusFormat, 
                                                    calculatorResponse.Status)); 
                if (calculatorResponse.Results != null &&  
                    calculatorResponse.Results.Count > 0) 
                { 
                    for (int i = 0; i < calculatorResponse.Results.Count; i++) 
                    { 
                        builder.AppendLine(string.Format(ResultFormat,  
                                                            i + 1,  
                                                            calculatorResponse.Results[i].Value, 
                                                            calculatorResponse.Results[i].Error)); 
                    } 
                } 
                builder.AppendLine(ReceivedMessagePropertiesHeader); 
                if (brokeredMessageProperty != null) 
                { 
                    foreach (var property in brokeredMessageProperty.Properties) 
                    { 
                        builder.AppendLine(string.Format(MessagePropertyFormat, 
                                                            property.Key, 
                                                            property.Value)); 
                    } 
                } 
                var traceMessage = builder.ToString(); 
                Trace.WriteLine(traceMessage.Substring(0, traceMessage.Length - 1)); 
 
                //Complete the Message 
                ReceiveContext receiveContext; 
                if (ReceiveContext.TryGet(incomingProperties, out receiveContext)) 
                { 
                    receiveContext.Complete(TimeSpan.FromSeconds(10.0d)); 
                } 
                else 
                { 
                    throw new InvalidOperationException("An exception occurred."); 
                } 
            } 
        } 
        catch (Exception ex) 
        { 
            Trace.WriteLine(ex.Message); 
        } 
    }  
    #endregion 
}

ListenUriEndpointBehavior

As you will see ahead in the article, it’s easy to define a WCF-Custom receive location to read messages from a Service Bus queue or from a subscription. Nevertheless, in the previous section we have seen that when defining a WCF service endpoint to consume messages from a subscription, you have to you have to specify the URL of the topic as the address of the service endpoint and the URL of the subscription as its listenUri. Now, when configuring a WCF receive location using the WCF-Custom adapter, there is a textbox for specifying the service endpoint address on the General tab of the configuration dialog, however there's no field to specify a value of the listenUri and ListenUriMode properties. For this reason, I decided to create a custom endpoint behavior that at runtime can set these values for a WCF-Custom receive location. The first attempt was to use the AddBindingParameters and ApplyDispatchBehavior methods exposed by the custom endpoint behavior to set the listenUri and ListenUriMode of the service endpoint passed as a parameter to both methods. However, this technique didn't work as expected.
Finally I solved the problem in the following way: I created a binding extension element class called ListenUriBehaviorExtensionElement to register the custom endpoint behavior in the machine.config file. This component exposes a property called listenUri that allows a user to specify the URL of a subscription when configuring a WCF-Custom receive location. At runtime, the ListenUriBehaviorExtensionElement component creates an instance of the ListenUriEndpointBehavior class. The AddBindingParameters method of the custom endpoint behavior replaces the original binding with a CustomBinding that contains the same binding elements and injects an instance of the ListenUriBindingElement at the top of the binding. This way, at runtime, the custom binding will be the first to execute. Finally, the BuildChannelListener method of the ListenUriBindingElement assigns the URL specified in the configuration of the receive location to the ListenUriBaseAddress property of the BindingContext and sets the value of its ListenUriMode property to Explicit. For you convenience, I included the code for the three classes below. Later in the article I'll show you how to use this component when defining a WCF-Custom receive location that receives messages from a Service Bus subscription.
C#
Edit|Remove
public class ListenUriBehaviorExtensionElement : IEndpointBehavior 
{ 
#region Private Constants 
    //*************************** 
    // Constants 
    //*************************** 
 
    private const string ListenUriName = "listenUri"; 
    private const string IsTrackingEnabledName = "isTrackingEnabled"; 
    private const string ListenUriDescription = "Gets or sets the URI at which the service endpoint listens."; 
    private const string IsTrackingEnabledDescription = "Gets or sets a value indicating whether tracking is enabled."; 
    #endregion 
 
    #region BehaviorExtensionElement Members 
    //*************************** 
    // Protected Methods 
    //*************************** 
 
    /// <summary> 
    /// Creates a behavior extension based on the current configuration settings. 
    /// </summary> 
    /// <returns>The behavior extension.</returns> 
    protected override object CreateBehavior() 
    { 
        return new ListenUriEndpointBehavior(ListenUri, IsTrackingEnabled); 
    } 
 
    /// <summary> 
    /// Gets the type of behavior. 
    /// </summary> 
    public override Type BehaviorType 
    { 
        get 
        { 
            return typeof(ListenUriEndpointBehavior); 
        } 
    }       
    #endregion 
 
    #region Public Properties 
    /// <summary> 
    /// Gets or sets the URI at which the service endpoint listens. 
    /// </summary> 
    [ConfigurationProperty(ListenUriName, IsRequired = true)] 
    [SettingsDescription(ListenUriDescription)] 
    public string ListenUri 
    { 
        get 
        { 
            return (string)base[ListenUriName]; 
        } 
        set 
        { 
            base[ListenUriName] = value; 
        } 
    } 
 
    /// <summary> 
    /// Gets or sets a value indicating whether the message inspector is enabled. 
    /// </summary> 
    [ConfigurationProperty(IsTrackingEnabledName, DefaultValue = true, IsRequired = false)] 
    [SettingsDescription(IsTrackingEnabledDescription)] 
    public bool IsTrackingEnabled 
    { 
        get 
        { 
            return (bool)base[IsTrackingEnabledName]; 
        } 
        set 
        { 
            base[IsTrackingEnabledName] = value; 
        } 
    } 
    #endregion 
} 
 
public class ListenUriEndpointBehavior : IEndpointBehavior 
{ 
    #region Private Constants 
    //*************************** 
    // Constants 
    //*************************** 
 
    private const string ListerUriMessageFormat = "[ListenUriEndpointBehavior] ListenUri = [{0}]."; 
    #endregion 
 
    #region Public Constructors 
    private readonly string listenUri; 
    private readonly bool isTrackingEnabled; 
    #endregion 
 
    #region Public Constructors 
    /// <summary> 
    /// Initializes a new instance of the ListenUriEndpointBehavior class. 
    /// </summary> 
    /// <param name="listenUri">The URI at which the service endpoint listens</param> 
    /// <param name="isTrackingEnabled">A boolean value indicating whether tracking is enabled</param> 
    public ListenUriEndpointBehavior(string listenUri, bool isTrackingEnabled) 
    { 
        this.listenUri = listenUri; 
        this.isTrackingEnabled = isTrackingEnabled; 
    } 
    #endregion 
 
    #region IEndpointBehavior Members 
    /// <summary> 
    /// Implement to pass data at runtime to bindings to support custom behavior. 
    /// </summary> 
    /// <param name="endpoint">The endpoint to modify.</param> 
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param> 
    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
        if (endpoint != null && 
            !String.IsNullOrEmpty(listenUri)) 
        { 
            // Read the binding elements from the original binding 
            var bindingElementCollection = endpoint.Binding.CreateBindingElements(); 
            // Create an array of binding elements 
            var bindingElementArray = new BindingElement[bindingElementCollection.Count + 1]; 
            // Add an instance of the ListenUriBindingElement as first binding element of the array 
            bindingElementArray[0] = new ListenUriBindingElement(listenUri); 
            // Copy the binding elements of the original binding to the array 
            bindingElementCollection.CopyTo(bindingElementArray, 1); 
            // Create a custom binding with the same binding elements as the original 
            // binding with the addition of the custom binding as first item 
            var customBinding = new CustomBinding(bindingElementArray) 
                                    { 
                                        CloseTimeout = endpoint.Binding.CloseTimeout, 
                                        OpenTimeout = endpoint.Binding.OpenTimeout, 
                                        ReceiveTimeout = endpoint.Binding.ReceiveTimeout, 
                                        SendTimeout = endpoint.Binding.SendTimeout, 
                                        Name = endpoint.Binding.Name, 
                                        Namespace = endpoint.Binding.Namespace 
                                    }; 
            //Replace the original binding with the newly created binding 
            endpoint.Binding = customBinding; 
            Trace.WriteLineIf(isTrackingEnabled, 
                                string.Format(ListerUriMessageFormat, 
                                              listenUri)); 
        } 
    } 
 
    /// <summary> 
    /// Implements a modification or extension of the client across an endpoint. 
    /// </summary> 
    /// <param name="endpoint">The endpoint that is to be customized.</param> 
    /// <param name="clientRuntime">The client runtime to be customized.</param> 
    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
    } 
 
    /// <summary> 
    /// Implements a modification or extension of the service across an endpoint. 
    /// </summary> 
    /// <param name="endpoint">The endpoint that exposes the contract.</param> 
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param> 
    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
    } 
 
    /// <summary> 
    /// Implement to confirm that the endpoint meets some intended criteria. 
    /// </summary> 
    /// <param name="endpoint">The endpoint to validate.</param> 
    void IEndpointBehavior.Validate(ServiceEndpoint endpoint) 
    { 
    } 
    #endregion 
} 
 
public class ListenUriBindingElement : BindingElement 
{ 
    #region Private Fields 
    private readonly string listenUri; 
    #endregion 
 
    #region Public Constructor 
    /// <summary> 
    /// Initializes a new instance of the ListenUriBindingElement class. 
    /// </summary> 
    /// <param name="listenUri">A BindingElement object that is a deep clone of the original.</param> 
    public ListenUriBindingElement(string listenUri) 
    { 
        this.listenUri = listenUri; 
    } 
    #endregion 
 
    #region BindingElement Members 
    /// <summary> 
    /// returns a copy of the binding element object. 
    /// </summary> 
    /// <returns></returns> 
    public override BindingElement Clone() 
    { 
        return new ListenUriBindingElement(listenUri); 
    } 
 
    /// <summary> 
    /// Returns a typed object requested, if present, from the appropriate layer in the binding stack. 
    /// </summary> 
    /// <typeparam name="T">The typed object for which the method is querying.</typeparam> 
    /// <param name="context">The BindingContext for the binding element.</param> 
    /// <returns>The typed object T requested if it is present or nullif it is not present.</returns> 
    public override T GetProperty<T>(BindingContext context) 
    { 
        return context.GetInnerProperty<T>(); 
    } 
 
    /// <summary> 
    /// Returns a value that indicates whether the binding element can build a  
    /// channel factory for a specific type of channel. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel the channel factory produces.</typeparam> 
    /// <param name="context">The BindingContext that provides context for the binding element.</param> 
    /// <returns>true if the IChannelFactory<TChannel/>of type TChannel can be built by  
    ///          the binding element; otherwise, false.</returns> 
    public override bool CanBuildChannelFactory<TChannel>(BindingContext context) 
    { 
        return false; 
    } 
 
    /// <summary> 
    /// Initializes a channel factory for producing channels of a specified type from the binding context. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel the factory builds.</typeparam> 
    /// <param name="context">The BindingContext that provides context for the binding element.</param> 
    /// <returns>The IChannelFactory<TChannel/>of type TChannel initialized from the context. </returns> 
    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) 
    { 
        throw new NotSupportedException(); 
    } 
 
    /// <summary> 
    /// Returns a value that indicates whether the binding element can build a channel  
    /// listener for a specific type of channel. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel listener to build.</typeparam> 
    /// <param name="context">The BindingContext for the binding element.</param> 
    /// <returns>true if a channel listener of the specified type can be built;  
    ///          otherwise, false. The default is false. </returns> 
    public override bool CanBuildChannelListener<TChannel>(BindingContext context) 
    { 
        return context.CanBuildInnerChannelListener<TChannel>(); 
    } 
 
    /// <summary> 
    /// Initializes a channel listener for producing channels of a specified type from the binding context. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel that the listener is built to accept.</typeparam> 
    /// <param name="context">The BindingContext for the binding element.</param> 
    /// <returns>The IChannelListener<TChannel/>of type IChannel initialized from the context.</returns> 
    public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) 
    { 
        if (!string.IsNullOrEmpty(listenUri)) 
        { 
            context.ListenUriBaseAddress = new Uri(listenUri); 
            context.ListenUriMode = ListenUriMode.Explicit; 
        } 
        return context.BuildInnerChannelListener<TChannel>(); 
    }  
    #endregion 
} 
You can enable the component tracking and use DebugView to monitor its runtime behavior.

 

SessionChannelEndpointBehavior

In order to receive messages from a sessionful queue or subscription, a WCF service needs to implement a session-aware contract interface like IOrderServiceSessionful in the following code snippet:
C#
Edit|Remove
// ServiceBus does not support IOutputSessionChannel. 
// All senders sending messages to sessionful queue must use a contract which does not enforce SessionMode.Required. 
// Sessionful messages are sent by setting the SessionId property of the BrokeredMessageProperty object. 
[ServiceContract] 
public interface IOrderService 
{ 
    [OperationContract(IsOneWay = true)] 
    [ReceiveContextEnabled(ManualControl = true)] 
    void ReceiveOrder(Order order); 
} 
 
// ServiceBus supports both IInputChannel and IInputSessionChannel.  
// A sessionful service listening to a sessionful queue must have SessionMode.Required in its contract. 
[ServiceContract(SessionMode = SessionMode.Required)] 
public interface IOrderServiceSessionful : IOrderService 
{ 
} 
 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)] 
public class OrderService : IOrderServiceSessionful 
{ 
    [OperationBehavior] 
    public void ReceiveOrder(Order order) 
    { 
        ... 
    } 
}
 WCF receive locations are implemented by a singleton instance of a WCF service class called BizTalkServiceInstance which implements multiple untyped, generic service contracts.
All of these interfaces are marked with the following attribute:
[ServiceContract(Namespace = http://www.microsoft.com/biztalk/2006/r2/wcf-adapter,
                         SessionMode = SessionMode.Allowed)]
In particular, the SessionMode property of the ServiceContract attribute is set to SessionMode.Allowed. This setting specifies that the contract supports sessions if the incoming binding supports them. In other words, all the service contracts implemented by the BizTalkServiceInstance support, but not require, a session.
When the following conditions are satisfied, the WCF runtime always selects the IInputChannel over the IInputSessionChannel when it selects the channel to use:
This rule implies that when you define a WCF-Custom receive location to consume messages from a sessionful queue or subscription, when you enable it, you will see an error like the following in the Application Log:
The adapter "WCF-Custom" raised an error message. Details "System.InvalidOperationException: It is not possible for an entity that requires sessions to create a non-sessionful message receiver.. TrackingId:9163a41f-d792-406d-acbe-eb93ab2defb8_1_1,TimeStamp:10/3/2011 12:39:02 PM ---> System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: It is not possible for an entity that requires sessions to create a non-sessionful message receiver.. TrackingId:9163a41f-d792-406d-acbe-eb93ab2defb8_1_1,TimeStamp:10/3/2011 12:39:02 PM
To avoid this problem, when we control the definition of the service contract, we can simply mark its ServiceContract attribute as requiring sessions. Unfortunately, we cannot change the definition of the service contracts implemented by the BizTalkServiceInstance class. So, how can we force a WCF receive location to use an IInputSessionChannel instead of an IInputChannel when consuming messages from a sessionful queue or subscription? Even in this case I solved the problem by using a WCF extension.
I created a binding extension element class called SessionChannelBehaviorExtensionElement to register the custom endpoint behavior in the machine.config. At runtime, this component creates an instance of the SessionChannelEndpointBehavior class. The AddBindingParameters method of the custom endpoint behavior replaces the original binding (in our case the NetMessagingBinding) with a CustomBinding that contains the same binding elements and injects an instance of the SessionChannelBindingElement at the top of the new binding. This way, at runtime, the custom binding will be the first to execute. Finally, I customized the BindingElement.CanBuildChannelListener<TChannel> method to return false when the type of channel is IInputChannel. In a nutshell, this component forces the WCF runtime to skip the IInputChannel. Hence, you can use it in a WCF-Custom receive location to force the WCF adapter to use the IInputSessionChannel. For your convenience, I included the code of the three classes below. Later in the article I'll show you how to use this component when defining a WCF-Custom receive location that consumes messages from a sessionful queue or subscription.
C#
Edit|Remove
public class SessionChannelBehaviorExtensionElement : BehaviorExtensionElement 
{ 
    #region Private Constants 
    //*************************** 
    // Constants 
    //*************************** 
 
    private const string IsTrackingEnabledName = "isTrackingEnabled"; 
    private const string IsTrackingEnabledDescription =  
                                              "Gets or sets a value indicating whether tracking is enabled."; 
    #endregion 
 
    #region BehaviorExtensionElement Members 
    //*************************** 
    // Protected Methods 
    //*************************** 
 
    /// <summary> 
    /// Creates a behavior extension based on the current configuration settings. 
    /// </summary> 
    /// <returns>The behavior extension.</returns> 
    protected override object CreateBehavior() 
    { 
        return new SessionChannelEndpointBehavior(IsTrackingEnabled); 
    } 
 
    /// <summary> 
    /// Gets the type of behavior. 
    /// </summary> 
    public override Type BehaviorType 
    { 
        get 
        { 
            return typeof(SessionChannelEndpointBehavior); 
        } 
    }       
    #endregion 
 
    #region Public Properties 
    /// <summary> 
    /// Gets or sets a value indicating whether the message inspector is enabled. 
    /// </summary> 
    [ConfigurationProperty(IsTrackingEnabledName, DefaultValue = true, IsRequired = false)] 
    [SettingsDescription(IsTrackingEnabledDescription)] 
    public bool IsTrackingEnabled 
    { 
        get 
        { 
            return (bool)base[IsTrackingEnabledName]; 
        } 
        set 
        { 
            base[IsTrackingEnabledName] = value; 
        } 
    } 
    #endregion 
} 
 
public class SessionChannelBindingElement : BindingElement 
{ 
    #region Private Constants 
    //*************************** 
    // Constants 
    //*************************** 
 
    private const string CanBuildIInputSessionChannel =  
                      "[SessionChannelBindingElement] CanBuildChannelListener returned true for InputSessionChannel."; 
    private const string CannotBuildIInputChannel =  
                      "[SessionChannelBindingElement] CanBuildChannelListener returned false for IInputChannel."; 
    #endregion 
 
    #region Private Fields 
    private readonly bool isTrackingEnabled; 
    #endregion 
 
    #region Public Constructor 
    /// <summary> 
    /// Initializes a new instance of the SessionChannelBindingElement class. 
    /// </summary> 
    /// <param name="isTrackingEnabled">A boolean value indicating whether tracking is enabled</param> 
    public SessionChannelBindingElement(bool isTrackingEnabled) 
    { 
        this.isTrackingEnabled = isTrackingEnabled; 
    } 
    #endregion 
 
    #region BindingElement Members 
    /// <summary> 
    /// returns a copy of the binding element object. 
    /// </summary> 
    /// <returns></returns> 
    public override BindingElement Clone() 
    { 
        return new SessionChannelBindingElement(isTrackingEnabled); 
    } 
 
    /// <summary> 
    /// Returns a typed object requested, if present, from the appropriate layer in the binding stack. 
    /// </summary> 
    /// <typeparam name="T">The typed object for which the method is querying.</typeparam> 
    /// <param name="context">The BindingContext for the binding element.</param> 
    /// <returns>The typed object T requested if it is present or nullif it is not present.</returns> 
    public override T GetProperty<T>(BindingContext context) 
    { 
        return context.GetInnerProperty<T>(); 
    } 
 
    /// <summary> 
    /// Returns a value that indicates whether the binding element can build  
    /// a channel factory for a specific type of channel. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel the channel factory produces.</typeparam> 
    /// <param name="context">The BindingContext that provides context for the binding element.</param> 
    /// <returns>true if the IChannelFactory<TChannel/>of type TChannel can be built by  
    ///          the binding element; otherwise, false.</returns> 
    public override bool CanBuildChannelFactory<TChannel>(BindingContext context) 
    { 
        return false; 
    } 
 
    /// <summary> 
    /// Initializes a channel factory for producing channels of a specified type from the binding context. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel the factory builds.</typeparam> 
    /// <param name="context">The BindingContext that provides context for the binding element.</param> 
    /// <returns>The IChannelFactory<TChannel/>of type TChannel initialized from the context. </returns> 
    public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) 
    { 
        throw new NotSupportedException(); 
    } 
 
    /// <summary> 
    /// Returns a value that indicates whether the binding element can build a  
    /// channel listener for a specific type of channel. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel listener to build.</typeparam> 
    /// <param name="context">The BindingContext for the binding element.</param> 
    /// <returns>true if a channel listener of the specified type can be built;  
    ///          otherwise, false. The default is false. </returns> 
    public override bool CanBuildChannelListener<TChannel>(BindingContext context) 
    { 
        var ok = typeof(TChannel) != typeof(IInputChannel) && context.CanBuildInnerChannelListener<TChannel>(); 
        Trace.WriteLineIf(isTrackingEnabled, ok ? CanBuildIInputSessionChannel : CannotBuildIInputChannel); 
        return ok; 
    } 
 
    /// <summary> 
    /// Initializes a channel listener for producing channels of a specified type from the binding context. 
    /// </summary> 
    /// <typeparam name="TChannel">The type of channel that the listener is built to accept.</typeparam> 
    /// <param name="context">The BindingContext for the binding element.</param> 
    /// <returns>The IChannelListener<TChannel/>of type IChannel initialized from the context.</returns> 
    public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) 
    { 
        return context.BuildInnerChannelListener<TChannel>(); 
    }  
    #endregion 
} 
 
You can enable the component tracking and use DebugView to monitor its runtime behavior.

Property Schemas

To achieve a full integration between the Service Bus and BizTalk Server, it’s necessary to translate the properties exposed by a BrokeredMessage into context properties of a BizTalk message and vice versa. The first step in this direction has been the definition of two property schemas:
 
 

Schemas

The request and response messages exchanged by the client and BizTalk applications have the following formats:
 CalculatorRequest
XML
Edit|Remove
<CalculatorRequest xmlns="http://windowsazure.cat.microsoft.com/samples/servicebus"  <Method>DynamicSendPortOrchestration</Method> 
  <Operations    <Operation      <Operator>+</Operator> 
      <Operand1>82</Operand1> 
      <Operand2>18</Operand2> 
    </Operation> 
    <Operation      <Operator>-</Operator> 
      <Operand1>30</Operand1> 
      <Operand2>12</Operand2> 
    </Operation> 
    <Operation      <Operator>*</Operator> 
      <Operand1>25</Operand1> 
      <Operand2>8</Operand2> 
    </Operation> 
    <Operation      <Operator>\</Operator> 
      <Operand1>100</Operand1> 
      <Operand2>25</Operand2> 
    </Operation> 
      <Operation      <Operator>+</Operator> 
      <Operand1>100</Operand1> 
      <Operand2>32</Operand2> 
    </Operation> 
  </Operations> 
</CalculatorRequest>
CalculatorResponse
XML
Edit|Remove
<CalculatorResponse xmlns="http://windowsazure.cat.microsoft.com/samples/servicebus"      <Status>Ok</Status> 
      <Results            <Result                  <Value>100</Value> 
                  <Error>None</Error> 
            </Result> 
            <Result                  <Value>18</Value> 
                  <Error>None</Error> 
            </Result> 
            <Result                  <Value>200</Value> 
                  <Error>None</Error> 
            </Result> 
            <Result                  <Value>4</Value> 
                  <Error>None</Error> 
            </Result> 
            <Result                  <Value>132</Value> 
                  <Error>None</Error> 
            </Result> 
      </Results> 
</CalculatorResponse>

ServiceBusMessageInspector

The final step to bridge the gap between the Service Bus and BizTalk Server has been to create a component to translate the properties of a BrokeredMessage into context properties of a BizTalk message and vice versa. Indeed, a WCF-Custom receive location can use the NetMessagingBinding to receive messages from a queue or subscription without the need to use any custom component to extend the normal behavior of the WCF adapter runtime. However, using this approach, a BizTalk application can read only the body of the message retrieved from the Service Bus, not the value of the properties specified by the sender. At the beginning of the article, I highlighted that it’s quite common to have a message with an empty body and the entire payload specified in the user defined properties. For this reason, it’s extremely important, not to say mandatory, to create a component that turns the properties of a BrokeredMessage into context properties of a BizTalk message and vice versa.
In the first part of the article we have seen that a WCF service which uses the NetMessagingBinding to read messages from a queue or a subscription, can retrieve a BrokeredMessageProperty object from the Properties collection of the incoming WCF message. This fact gives us the possibility to write a custom message inspector to intercept the incoming WCF message and read the BrokeredMessageProperty from its properties. For obvious reasons, this operation cannot be done using a custom pipeline component because a custom pipeline does not provide access to the WCF message. In the previous step we created the property schemas which define, respectively, the object specific and user defined properties for Service Bus messages. The last piece to complete the puzzle is to find a way to promote or write a property to the BizTalk message context. Fortunately, the WCF adapter runtime provides an easy way to accomplish this task.
As explained in the BizTalk Server documentation on MSDN, to promote a set of properties to the BizTalk message context you need to add their xml qualified name and value to a collection of key/value pairs. Next you must add a new property to the inbound WCF message whose key must be equal to:
and specify the collection of key/value pairs as its value. The WCF adapter looks for this property in the WCF message and will promote all the properties contained in the collection. Likewise, to write but not promote a set of properties to the BizTalk message context, you need to add their XML qualified name and value to another collection of key/value pairs. Next you must add another property to the inbound WCF message whose key must be equal to:
and specify the second collection of key/value pairs as its value. The WCF adapter looks for this property in the WCF message and writes all the properties contained in the collection to the BizTalk message context.
The ServiceBusMessageInspector component exploits this feature to promote or write the properties of the BrokeredMessageProperty to the context of the BizTalk message when a WCF-Custom receive location reads a message from a Service Bus queue or subscription. Conversely, the component uses another characteristic of the WCF adapter to retrieve the context property from an outbound WCF message and translate them into properties of a BrokeredMessageProperty. In fact, the send handler of the WCF adapter copies all the message context properties into the Properties collection when it creates a WCF message from a BizTalk message. Therefore, when using a WCF-Custom send port, this allows access to any message context properties within a custom message inspector or channel component at runtime.
You can enable the component tracking and use DebugView to monitor its runtime behavior. For you convenience, I included the code of the custom message inspector in the following example.
 
C#
Edit|Remove
/// <summary>/// This class is used to promote\write properties to the message context./// </summary>publicclass ServiceBusMessageInspector : IDispatchMessageInspector, IClientMessageInspector 
{ 
    #region Private Constants//***************************// Constants//***************************privateconststring Source = "ServiceBusMessageInspector"; 
    privateconststring BrokeredMessagePropertySchemaNamespace = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property"; 
    privateconststring PropertiesToPromoteKey = "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties/Promote"; 
    privateconststring PropertiesToWriteKey = "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties/WriteToContext"; 
    privateconststring ContentTypeProperty = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property#ContentType"; 
    privateconststring CorrelationIdProperty = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property#CorrelationId"; 
    privateconststring LabelProperty = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property#Label"; 
    privateconststring ReplyToProperty = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property#ReplyTo"; 
    privateconststring ReplyToSessionIdProperty = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property#ReplyToSessionId"; 
    privateconststring SessionIdProperty = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property#SessionId"; 
    privateconststring ToProperty = "http://schemas.microsoft.com/servicebusforwindowsserver/2013/brokered-message-property#To"; 
    privateconststring ContentType = "ContentType"; 
    privateconststring CorrelationId = "CorrelationId"; 
    privateconststring DeliveryCount = "DeliveryCount"; 
    privateconststring EnqueuedTimeUtc = "EnqueuedTimeUtc"; 
    privateconststring ExpiresAtUtc = "ExpiresAtUtc"; 
    privateconststring Label = "Label"; 
    privateconststring MessageId = "MessageId"; 
    privateconststring ReplyTo = "ReplyTo"; 
    privateconststring ReplyToSessionId = "ReplyToSessionId"; 
    privateconststring ScheduledEnqueueTimeUtc = "ScheduledEnqueueTimeUtc"; 
    privateconststring SequenceNumber = "SequenceNumber"; 
    privateconststring SessionId = "SessionId"; 
    privateconststring TimeToLive = "TimeToLive"; 
    privateconststring To = "To"; 
    privateconststring PropertiesToPromote = "PropertiesToPromote"; 
    privateconststring PropertiesToWrite = "PropertiesToWrite"; 
    privateconststring PropertiesToSend = "PropertiesToSend"; 
 
    //***************************// Messages//***************************privateconststring PropertiesElementCannotBeNull = "[ServiceBusMessageInspector] The PropertiesElement object cannot be null."; 
    privateconststring NameAndNamespaceNumberAreDifferent = "[ServiceBusMessageInspector] The number of items in the name and namespace comma-separated lists is different in the {0} property."; 
    privateconststring PropertyAddedToList = "[ServiceBusMessageInspector] The following property was added to the [{0}] list:\n\r - Name=[{1}]\n\r - Namespace=[{2}]\n\r - Value=[{3}]"; 
    privateconststring ExceptionFormat = "[ServiceBusMessageInspector] The following exception occurred=[{0}]."; 
    #endregion 
 
    #region Private Fields//***************************// Private Fields//***************************privatereadonlybool isComponentEnabled; 
    privatereadonlybool isTrackingEnabled; 
    privatereadonly Dictionary<stringstring> propertiesToPromoteDictionary = new Dictionary<stringstring>(); 
    privatereadonly Dictionary<stringstring> propertiesToWriteDictionary = new Dictionary<stringstring>(); 
    privatereadonly Dictionary<stringstring> propertiesToSendDictionary = new Dictionary<stringstring>(); 
    #endregion 
 
    #region Public Constructors//***************************// Public Constructors//***************************/// <summary>/// Initializes a new instance of the ServiceBusMessageInspector class./// </summary>public ServiceBusMessageInspector() 
    { 
        isComponentEnabled = true; 
    } 
 
    /// <summary>/// Initializes a new instance of the ServiceBusMessageInspector class./// </summary>/// <param name="isComponentEnabled">This value indicates whether the message inspector is enabled.</param>/// <param name="isTrackingEnabled">This value indicates whether tracking is enabled.</param>/// <param name="propertiesToPromote">This object defines the properties to promote.</param>/// <param name="propertiesToWrite">This object defines the properties to write.</param>/// <param name="propertiesToSend">This object defines the properties to send.</param>public ServiceBusMessageInspector(bool isComponentEnabled, 
                                        bool isTrackingEnabled, 
                                        PropertiesElement propertiesToPromote, 
                                        PropertiesElement propertiesToWrite, 
                                        PropertiesElement propertiesToSend) 
    { 
        this.isComponentEnabled = isComponentEnabled; 
        this.isTrackingEnabled = isTrackingEnabled; 
 
        // Propulate the dictionary that contains the properties to promote 
        InitializeDictionary(PropertiesToPromote, propertiesToPromote, propertiesToPromoteDictionary); 
 
        // Propulate the dictionary that contains the properties to write 
        InitializeDictionary(PropertiesToWrite, propertiesToWrite, propertiesToWriteDictionary); 
 
        // Propulate the dictionary that contains the properties to send out 
        InitializeDictionary(PropertiesToSend, propertiesToSend, propertiesToSendDictionary); 
    } 
    #endregion 
 
    #region IDispatchMessageInspector Members//**********************************// IDispatchMessageInspector Members//**********************************/// <summary>/// Called after an inbound message has been received but /// before the message is dispatched to the intended operation./// This method extracts the properties from the BrokeredMessageProperty /// object contained in the WCF message properties and write or promote /// them in the message context./// </summary>/// <param name="request">The request message.</param>/// <param name="channel">The incoming channel.</param>/// <param name="instanceContext">The current service instance.</param>/// <returns>The object used to correlate state. This object is passed back in the BeforeSendReply method.</returns>publicobject AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) 
    { 
        try 
        { 
            if (!isComponentEnabled) 
            { 
                return Guid.NewGuid(); 
            } 
 
            var propertiesToPromoteList = new List<KeyValuePair<XmlQualifiedName, object>>(); 
            var propertiesToWriteList = new List<KeyValuePair<XmlQualifiedName, object>>(); 
 
            // Retreive the BrokeredMessageProperty from the current operation context 
            var incomingProperties = OperationContext.Current.IncomingMessageProperties; 
            var brokeredMessageProperty = (BrokeredMessageProperty)incomingProperties[BrokeredMessageProperty.Name]; 
 
            if (brokeredMessageProperty != null) 
            { 
                // Include BrokeredMessageProperty explicit properties // in the list of properties to write to the BizTalk message contextif (!string.IsNullOrEmpty(brokeredMessageProperty.ContentType)) 
                { 
                    AddItemToList(ContentType,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.ContentType,  
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
                if (!string.IsNullOrEmpty(brokeredMessageProperty.CorrelationId)) 
                { 
                    AddItemToList(CorrelationId,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.CorrelationId, 
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
                AddItemToList(DeliveryCount,  
                                BrokeredMessagePropertySchemaNamespace,  
                                brokeredMessageProperty.DeliveryCount, 
                                PropertiesToWrite, 
                                propertiesToWriteList); 
                AddItemToList(EnqueuedTimeUtc,  
                                BrokeredMessagePropertySchemaNamespace,  
                                brokeredMessageProperty.EnqueuedTimeUtc, 
                                PropertiesToWrite, 
                                propertiesToWriteList); 
                AddItemToList(ExpiresAtUtc,  
                                BrokeredMessagePropertySchemaNamespace,  
                                brokeredMessageProperty.ExpiresAtUtc, 
                                PropertiesToWrite, 
                                propertiesToWriteList); 
                if (!string.IsNullOrEmpty(brokeredMessageProperty.Label)) 
                { 
                    AddItemToList(Label,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.Label, 
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
                     
                if (!string.IsNullOrEmpty(brokeredMessageProperty.MessageId)) 
                { 
                    AddItemToList(MessageId,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.MessageId, 
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
                if (!string.IsNullOrEmpty(brokeredMessageProperty.ReplyTo)) 
                { 
                    AddItemToList(ReplyTo,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.ReplyTo, 
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
                if (!string.IsNullOrEmpty(brokeredMessageProperty.ReplyToSessionId)) 
                { 
                    AddItemToList(ReplyToSessionId,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.ReplyToSessionId, 
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
                AddItemToList(ScheduledEnqueueTimeUtc,  
                                BrokeredMessagePropertySchemaNamespace,  
                                brokeredMessageProperty.ScheduledEnqueueTimeUtc, 
                                PropertiesToWrite, 
                                propertiesToWriteList); 
                AddItemToList(SequenceNumber,  
                                BrokeredMessagePropertySchemaNamespace,  
                                brokeredMessageProperty.SequenceNumber, 
                                PropertiesToWrite, 
                                propertiesToWriteList); 
                if (!string.IsNullOrEmpty(brokeredMessageProperty.SessionId)) 
                { 
                    AddItemToList(SessionId,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.SessionId, 
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
                AddItemToList(TimeToLive,  
                                BrokeredMessagePropertySchemaNamespace,  
                                brokeredMessageProperty.TimeToLive.TotalSeconds, 
                                PropertiesToWrite, 
                                propertiesToWriteList); 
                if (!string.IsNullOrEmpty(brokeredMessageProperty.To)) 
                { 
                    AddItemToList(To,  
                                    BrokeredMessagePropertySchemaNamespace,  
                                    brokeredMessageProperty.To, 
                                    PropertiesToWrite, 
                                    propertiesToWriteList); 
                } 
 
                // Promote or write properties indicated in the// configuration of the message inspector.if (brokeredMessageProperty.Properties != null && 
                    brokeredMessageProperty.Properties.Count > 0) 
                { 
                    foreach (var property in brokeredMessageProperty.Properties) 
                    { 
                        if (propertiesToPromoteDictionary.ContainsKey(property.Key)) 
                        { 
                            AddItemToList(property.Key, 
                                            propertiesToPromoteDictionary[property.Key], 
                                            property.Value, 
                                            PropertiesToPromote, 
                                            propertiesToPromoteList); 
                            continue; 
                        } 
                        if (propertiesToWriteDictionary.ContainsKey(property.Key)) 
                        { 
                            AddItemToList(property.Key, 
                                            propertiesToWriteDictionary[property.Key], 
                                            property.Value, 
                                            PropertiesToWrite, 
                                            propertiesToWriteList); 
                        } 
                    } 
                } 
            } 
 
            // Notify the WCF Adapter the properties to promoteif (propertiesToPromoteList.Count > 0) 
            { 
                request.Properties.Add(PropertiesToPromoteKey, propertiesToPromoteList); 
            } 
 
            // Notify the WCF Adapter the properties to writeif (propertiesToWriteList.Count > 0) 
            { 
                request.Properties.Add(PropertiesToWriteKey, propertiesToWriteList); 
            } 
        } 
        catch (Exception ex) 
        { 
            Trace.WriteLineIf(isTrackingEnabled, string.Format(ExceptionFormat, ex.Message)); 
            EventLog.WriteEntry(Source, ex.Message, EventLogEntryType.Error); 
            throw; 
        } 
        return Guid.NewGuid(); 
    } 
 
    /// <summary>/// Called after the operation has returned but before the reply message is sent. /// </summary>/// <param name="reply">The reply message. This value is null if the operation is one way.</param>/// <param name="correlationState">The correlation object returned from the AfterReceiveRequest method.</param>publicvoid BeforeSendReply(ref Message reply, object correlationState) 
    { 
    } 
    #endregion 
 
    #region IClientMessageInspector Members//**********************************// IClientMessageInspector Members//**********************************/// <summary>/// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application. /// </summary>/// <param name="reply">The message to be transformed into types and handed back to the client application.</param>/// <param name="correlationState">Correlation state data.</param>publicvoid AfterReceiveReply(ref Message reply, object correlationState) 
    { 
    } 
 
    /// <summary>/// Enables inspection or modification of a message before a request message is sent to a service./// </summary>/// <param name="message">The message to be sent to the service.</param>/// <param name="channel">The client object channel.</param>/// <returns>The object that is returned as the correlationState argument of the AfterReceiveReply method.</returns>publicobject BeforeSendRequest(ref Message message, IClientChannel channel) 
    { 
        try 
        { 
            if (!isComponentEnabled) 
            { 
                return message.Headers.Action; 
            } 
 
            // Create a new BrokeredMessageProperty object 
            var brokeredMessageProperty = new BrokeredMessageProperty(); 
 
            // Adds properties to the BrokeredMessageProperty objectforeach (var property in message.Properties) 
            { 
                if (string.Compare(property.Key, ContentTypeProperty, StringComparison.OrdinalIgnoreCase) == 0) 
                { 
                    brokeredMessageProperty.ContentType = message.Properties[ContentTypeProperty] asstring; 
                    Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList, 
                                                                        PropertiesToSend, 
                                                                        ContentType, 
                                                                        BrokeredMessagePropertySchemaNamespace, 
                                                                        brokeredMessageProperty.ContentType)); 
                    continue; 
                } 
                if (string.Compare(property.Key, CorrelationIdProperty, StringComparison.OrdinalIgnoreCase) == 0) 
                { 
                    brokeredMessageProperty.CorrelationId = message.Properties[CorrelationIdProperty] asstring; 
                    Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList, 
                                                                        PropertiesToSend, 
                                                                        CorrelationId, 
                                                                        BrokeredMessagePropertySchemaNamespace, 
                                                                        brokeredMessageProperty.CorrelationId)); 
                    continue; 
                } 
                if (string.Compare(property.Key, LabelProperty, StringComparison.OrdinalIgnoreCase) == 0) 
                { 
                    brokeredMessageProperty.Label = message.Properties[LabelProperty] asstring; 
                    Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList, 
                                                                        PropertiesToSend, 
                                                                        Label, 
                                                                        BrokeredMessagePropertySchemaNamespace, 
                                                                        brokeredMessageProperty.Label)); 
                    continue; 
                } 
                if (string.Compare(property.Key, ReplyToProperty, StringComparison.OrdinalIgnoreCase) == 0) 
                { 
                    brokeredMessageProperty.ReplyTo = message.Properties[ReplyToProperty] asstring; 
                    Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList, 
                                                                        PropertiesToSend, 
                                                                        ReplyTo, 
                                                                        BrokeredMessagePropertySchemaNamespace, 
                                                                        brokeredMessageProperty.ReplyTo)); 
                    continue; 
                } 
                if (string.Compare(property.Key, ReplyToSessionIdProperty, StringComparison.OrdinalIgnoreCase) == 0) 
                { 
                    brokeredMessageProperty.ReplyToSessionId = message.Properties[ReplyToSessionIdProperty] asstring; 
                    Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList, 
                                                                        PropertiesToSend, 
                                                                        ReplyToSessionId, 
                                                                        BrokeredMessagePropertySchemaNamespace, 
                                                                        brokeredMessageProperty.ReplyToSessionId)); 
                    continue; 
                } 
                if (string.Compare(property.Key, SessionIdProperty, StringComparison.OrdinalIgnoreCase) == 0) 
                { 
                    brokeredMessageProperty.SessionId = message.Properties[SessionIdProperty] asstring; 
                    Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList, 
                                                                        PropertiesToSend, 
                                                                        SessionId, 
                                                                        BrokeredMessagePropertySchemaNamespace, 
                                                                        brokeredMessageProperty.SessionId)); 
                    continue; 
                } 
                if (string.Compare(property.Key, ToProperty, StringComparison.OrdinalIgnoreCase) == 0) 
                { 
                    brokeredMessageProperty.To = message.Properties[ToProperty] asstring; 
                    Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList, 
                                                                        PropertiesToSend, 
                                                                        To,  
                                                                        BrokeredMessagePropertySchemaNamespace, 
                                                                        brokeredMessageProperty.To)); 
                    continue; 
                } 
 
                // Include properties indicated in the// configuration of the message inspector. 
                var parts = property.Key.Split('#'); 
                if (parts.Length == 2) 
                { 
                    if (propertiesToSendDictionary.ContainsKey(parts[1]) && 
                        propertiesToSendDictionary[parts[1]] == parts[0]) 
                    { 
                        brokeredMessageProperty.Properties[parts[1]] = property.Value; 
                        Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList,  
                                                                            PropertiesToSend,  
                                                                            parts[0],  
                                                                            parts[1], 
                                                                            property.Value)); 
                    } 
                } 
            } 
 
            // Add the BrokeredMessageProperty to the WCF message properties 
            message.Properties[BrokeredMessageProperty.Name] = brokeredMessageProperty; 
        } 
        catch (Exception ex) 
        { 
            Trace.WriteLineIf(isTrackingEnabled, string.Format(ExceptionFormat, ex.Message)); 
            EventLog.WriteEntry(Source, ex.Message, EventLogEntryType.Error); 
            throw; 
        } 
        return Guid.NewGuid(); 
    } 
    #endregion 
 
    #region Private Methods//******************// Private Methods//******************/// <summary>/// Initialize the dictionary passed as second parameter /// with the information read from the first argument./// In particular, the Name and Namespace properties of the /// PropertiesElement object contain respectively /// the comma-separated list of property names and namespaces/// of a set of properties. The name of each property will/// become the key in the dictionary, whereas the namespace/// will become the corresponding value./// </summary>/// <param name="dictionaryName">The name of the dictionary.</param>/// <param name="properties">A PropertiesElement object.</param>/// <param name="dictionary">The dictionary to populate.</param>privatevoid InitializeDictionary(string dictionaryName, PropertiesElement properties, Dictionary<stringstring> dictionary) 
    { 
        if (properties == null) 
        { 
            Trace.WriteLineIf(isTrackingEnabled, PropertiesElementCannotBeNull); 
            thrownew ApplicationException(PropertiesElementCannotBeNull); 
        } 
        if (!string.IsNullOrEmpty(properties.Name) && 
            !string.IsNullOrEmpty(properties.Namespace)) 
        { 
            var nameArray = properties.Name.Split(new[] { ','';''|' }); 
            var namespaceArray = properties.Namespace.Split(new[] { ','';''|' }); 
            if (namespaceArray.Length == nameArray.Length) 
            { 
                if (nameArray.Length > 0) 
                { 
                    for (int i = 0; i < nameArray.Length; i++) 
                    { 
                        dictionary[nameArray[i]] = namespaceArray[i]; 
                    } 
                } 
            } 
            else 
            { 
                Trace.WriteLineIf(isTrackingEnabled, string.Format(NameAndNamespaceNumberAreDifferent, dictionaryName)); 
                thrownew ApplicationException(string.Format(NameAndNamespaceNumberAreDifferent, dictionaryName)); 
            } 
        } 
    } 
 
    /// <summary>/// Adds a new property to the specified list./// </summary>/// <param name="name">The name of a property.</param>/// <param name="ns">The namespace of a property.</param>/// <param name="value">The value of a property.</param>/// <param name="listName">The name of the list.</param>/// <param name="list">The list to add the property to.</param>privatevoid AddItemToList(string name,  
                                string ns,  
                                objectvalue,  
                                string listName,  
                                List<KeyValuePair<XmlQualifiedName, object>> list) 
    { 
        list.Add(new KeyValuePair<XmlQualifiedName, object>(new XmlQualifiedName(name, ns), value)); 
        Trace.WriteLineIf(isTrackingEnabled, string.Format(PropertyAddedToList,  
                                                            listName,  
                                                            name,  
                                                            ns,  
                                                            value)); 
    } 
    #endregion 
}    
The constructor of the message inspector reads the configuration data and initializes three dictionaries that contain, respectively, the properties to promote, to write, and to send out.
  1.  
    1. Creates a new  BrokeredMessageProperty  object.
    2. Reads the context properties, defined in the BrokeredMessagePropertySchema, from the Properties collection of the WCF message and assigns their values to the corresponding properties of the BrokeredMessageProperty.
    3. Adds the custom context properties, in our sample defined in the PropertySchema, to the Properties collection of the BrokeredMessageProperty.
    4. Finally, adds the BrokeredMessageProperty to the WCF message properties.
You can enable the component tracking and use DebugView, as shown in the picture below, to monitor its runtime behavior.
 
If you stop both orchestrations and use the Service Bus Explorer or client application to send a request message to the requestqueue or requesttopic, the message is read by one of the WCF receive location and suspended because no subscribers is available. This allows you to use the BizTalk Server Administration Console to examine the context properties, in particular, those promoted or wriiten by the ServiceBusMessageInspector, highlighted in green (BrokeredMessagePropertySchema) and yellow (PropertySchema) in the picture below.

TokenProviderEndpointBehavior

In order to exchange messages with a Service Bus for Windows Server farm via WCF, an application needs to use the NetMessagingBinding. In particular, client and service WCF endpoints need to be configured to use the TransportClientEndpointBehavior with the WindowsTokenProvider or OAuthTokenProvider to authenticate against the STS used by the Service Bus farm. Therefore, it would be sufficient to configure WCF-Custom receive locations and send ports to directly use the TransportClientEndpointBehavior. The problem is that both the WindowsTokenProvider and OAuthTokenProvider require to specify a list STS URIs. This URI of the STS endpoint can be easily determined from the connection string of the Service Bus namespace that in turn can be obtained by using the Get-SBClientConfiguration PowerShell cmdlet, as shown in the following script.
 
PowerShell
Edit|Remove
# Import Windows Azure PowerShell Module 
Import-Module  "C:\Program Files\Service Bus\1.0\ServiceBus\ServiceBus.psd1" -ErrorAction Stop 
 
# Retrieve the connection string for the namespace 
Get-SBClientConfiguration -Namespaces ServiceBusDefaultNamespace
The problem is that, if you select the TransportClientEndpointBehavior in the configuration of a WCF-Custom receive location or send port, as shown in the following picture, you can't add any address to the StsUris array.
For this reason, I created a custom endpoint behavior called TokenProviderEndpointBehavior that allows to define the following information in the configuration of a WCF-Custom receive location or send port:
The following picture shows how to use this component in a WCF-Custom receive location or send port.
 
For your convenience, I included below the code of this component.
C#
Edit|Remove
public enum TokenProviderType 
{ 
    WindowsTokenProvider, 
    OAuthTokenProvider 
} 
 
public class TokenProviderBehaviorExtensionElement : BehaviorExtensionElement 
{ 
    #region Private Constants 
    //*************************** 
    // Constants 
    //*************************** 
 
    private const string UserNamePropertyName = "userName"; 
    private const string PasswordPropertyName = "password"; 
    private const string DomainPropertyName = "domain"; 
    private const string StsEndpointPropertyName = "stsEndpoint"; 
    private const string TokenProviderTypePropertyName = "tokenProviderType"; 
    private const string IsTrackingEnabledPropertyName = "isTrackingEnabled"; 
    private const string UserNamePropertyDescription = "Gets or sets the user name associated with the credentials."; 
    private const string PasswordPropertyDescription = "Gets or sets the password for the user name associated with the credentials."; 
    private const string DomainPropertyDescription = "Gets or sets the domain associated with these credentials."; 
    private const string StsEndpointPropertyDescription = "Gets or sets the uri of the STS endpoint."; 
    private const string TokenProviderTypePropertyDescription = "Gets or sets the token provider type."; 
    private const string IsTrackingEnabledPropertyDescription = "Gets or sets a value indicating whether tracking is enabled."; 
    #endregion 
 
    #region BehaviorExtensionElement Members 
    //*************************** 
    // Protected Methods 
    //*************************** 
 
    /// <summary> 
    /// Creates a behavior extension based on the current configuration settings. 
    /// </summary> 
    /// <returns>The behavior extension.</returns> 
    protected override object CreateBehavior() 
    { 
        return new TokenProviderEndpointBehavior(UserName, Password, Domain, StsEndpoint, TokenProviderType, IsTrackingEnabled); 
    } 
 
    /// <summary> 
    /// Gets the type of behavior. 
    /// </summary> 
    public override Type BehaviorType 
    { 
        get 
        { 
            return typeof(TokenProviderEndpointBehavior); 
        } 
    }       
    #endregion 
 
    #region Public Properties 
    /// <summary> 
    /// Gets or sets the user name associated with the credentials. 
    /// </summary> 
    [ConfigurationProperty(UserNamePropertyName, IsRequired = false)] 
    [SettingsDescription(UserNamePropertyDescription)] 
    public string UserName 
    { 
        get 
        { 
            return (string)base[UserNamePropertyName]; 
        } 
        set 
        { 
            base[UserNamePropertyName] = value; 
        } 
    } 
 
    /// <summary> 
    /// Gets or sets the password for the user name associated with the credentials. 
    /// </summary> 
    [ConfigurationProperty(PasswordPropertyName, IsRequired = false)] 
    [SettingsDescription(PasswordPropertyDescription)] 
    public string Password 
    { 
        get 
        { 
            return (string)base[PasswordPropertyName]; 
        } 
        set 
        { 
            base[PasswordPropertyName] = value; 
        } 
    } 
 
    /// <summary> 
    /// Gets or sets the domain associated with these credentials. 
    /// </summary> 
    [ConfigurationProperty(DomainPropertyName, IsRequired = false)] 
    [SettingsDescription(DomainPropertyDescription)] 
    public string Domain 
    { 
        get 
        { 
            return (string)base[DomainPropertyName]; 
        } 
        set 
        { 
            base[DomainPropertyName] = value; 
        } 
    } 
 
    /// <summary> 
    /// Gets or sets the uri of the STS endpoint. 
    /// </summary> 
    [ConfigurationProperty(StsEndpointPropertyName, IsRequired = true)] 
    [SettingsDescription(StsEndpointPropertyDescription)] 
    public string StsEndpoint 
    { 
        get 
        { 
            return (string)base[StsEndpointPropertyName]; 
        } 
        set 
        { 
            base[StsEndpointPropertyName] = value; 
        } 
    } 
 
    /// <summary> 
    /// Gets or sets the STS port. 
    /// </summary> 
    [ConfigurationProperty(TokenProviderTypePropertyName, DefaultValue = TokenProviderType.WindowsTokenProvider, IsRequired = true)] 
    [SettingsDescription(TokenProviderTypePropertyDescription)] 
    public TokenProviderType TokenProviderType 
    { 
        get 
        { 
            return (TokenProviderType)base[TokenProviderTypePropertyName]; 
        } 
        set 
        { 
            base[TokenProviderTypePropertyName] = value; 
        } 
    } 
 
    /// <summary> 
    /// Gets or sets a value indicating whether tracking is enabled. 
    /// </summary> 
    [ConfigurationProperty(IsTrackingEnabledPropertyName, DefaultValue = true, IsRequired = false)] 
    [SettingsDescription(IsTrackingEnabledPropertyDescription)] 
    public bool IsTrackingEnabled 
    { 
        get 
        { 
            return (bool)base[IsTrackingEnabledPropertyName]; 
        } 
        set 
        { 
            base[IsTrackingEnabledPropertyName] = value; 
        } 
    } 
    #endregion 
} 
 
public class TokenProviderEndpointBehavior : IEndpointBehavior 
{ 
    #region Private Constants 
    //*************************** 
    // Constants 
    //*************************** 
 
    private const string MessageFormat = "[TokenProviderEndpointBehavior] TokenProvider = {0}."; 
    #endregion 
 
    #region Private Fields 
    private readonly string userName; 
    private readonly string password; 
    private readonly string domain; 
    private readonly string stsEndpoint; 
    private readonly TokenProviderType tokenProviderType; 
    private readonly bool isTrackingEnabled; 
    #endregion 
 
    #region Public Constructors 
    /// <summary> 
    /// Initializes a new instance of the TokenProviderEndpointBehavior class. 
    /// </summary> 
    /// <param name="userName">The user name associated with the credentials.</param> 
    /// <param name="password">The password for the user name associated with the credentials.</param> 
    /// <param name="domain">The domain associated with these credentials.</param> 
    /// <param name="stsEndpoint">The uri of the sts endpoint.</param> 
    /// <param name="tokenProviderType">The type of the token provider.</param> 
    /// <param name="isTrackingEnabled">A boolean value indicating whether tracking is enabled.</param> 
    public TokenProviderEndpointBehavior(string userName,  
                                            string password, 
                                            string domain, 
                                            string stsEndpoint, 
                                            TokenProviderType tokenProviderType, 
                                            bool isTrackingEnabled) 
    { 
        this.userName = userName; 
        this.password = password; 
        this.domain = domain; 
        this.stsEndpoint = stsEndpoint; 
        this.tokenProviderType = tokenProviderType; 
        this.isTrackingEnabled = isTrackingEnabled; 
    } 
    #endregion 
 
    #region IEndpointBehavior Members 
    /// <summary> 
    /// Implement to pass data at runtime to bindings to support custom behavior. 
    /// </summary> 
    /// <param name="endpoint">The endpoint to modify.</param> 
    /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param> 
    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
        var stsUri = new Uri(stsEndpoint); 
        var stsUris = new List<Uri> { stsUri }; 
        var useCurrentUser = string.IsNullOrEmpty(userName) || 
                                string.IsNullOrEmpty(password) || 
                                string.IsNullOrEmpty(domain); 
        bindingParameters.Add(new TransportClientEndpointBehavior 
            { 
                TokenProvider = tokenProviderType == TokenProviderType.WindowsTokenProvider ? 
                                useCurrentUser ? TokenProvider.CreateWindowsTokenProvider(stsUris) : TokenProvider.CreateWindowsTokenProvider(stsUris, new NetworkCredential(userName, password, domain)) : 
                                TokenProvider.CreateOAuthTokenProvider(stsUris, new NetworkCredential(userName, password, domain)) 
            }); 
        Trace.WriteLineIf(isTrackingEnabled, string.Format(MessageFormat, tokenProviderType)); 
    } 
 
    /// <summary> 
    /// Implements a modification or extension of the client across an endpoint. 
    /// </summary> 
    /// <param name="endpoint">The endpoint that is to be customized.</param> 
    /// <param name="clientRuntime">The client runtime to be customized.</param> 
    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
    } 
 
    /// <summary> 
    /// Implements a modification or extension of the service across an endpoint. 
    /// </summary> 
    /// <param name="endpoint">The endpoint that exposes the contract.</param> 
    /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param> 
    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
    } 
 
    /// <summary> 
    /// Implement to confirm that the endpoint meets some intended criteria. 
    /// </summary> 
    /// <param name="endpoint">The endpoint to validate.</param> 
    void IEndpointBehavior.Validate(ServiceEndpoint endpoint) 
    { 
    } 
    #endregion 
}
 

Installing and configuring components

In order to use the WCF extensions in a BizTalk application, you need to perform the following operations:
  1. Install the solution assemblies in the global assembly cache (GAC).
  2. Register the components in the proper machine.config file on the BizTalk Server computer.
  3. Configure BizTalk Server to use the proper version of the Microsoft.ServiceBus.dll.
The first step is pretty straightforward and can be accomplished using the gacutil tool. You can automatically install your assembly to the GAC whenever you build the class library by including the execution of the gacutil tool in the post-build event command-line of the project, as shown in following picture:
An easy and handy way to verify that the an assembly has been successfully installed in the GAC is to use the following command:
gacutil /lr <assemblyName>
The second step consists in registering the Service Bus and solution WCF extensions to be used by BizTalk Server. To accomplish this task you can use two mechanisms:
 
Whatever mechanism you choose to adopt, you can use the WCFServiceBusExtensions.config and WCFSolutionExtensions.config files in the Setup folder respectively to register the WCF extensions introduced by the Service Bus and those defined used by my solution, or use the file WCFServiceBusAndSolutionExtensions.config to configure them altogether.
 
WCFServiceBusExtensions.config
XML
Edit|Remove
<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
  <system.serviceModel> 
    <extensions> 
        <behaviorExtensions> 
            <add name="connectionStatusBehavior" type="Microsoft.ServiceBus.Configuration.ConnectionStatusElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        </behaviorExtensions> 
        <bindingElementExtensions> 
            <add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        </bindingElementExtensions> 
        <bindingExtensions> 
            <add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
            <add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElementMicrosoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> 
        </bindingExtensions> 
    </extensions> 
    <client> 
        <endpoint address="" binding="netTcpRelayBinding" contract="IMetadataExchange" name="sb"/> 
    </client> 
  </system.serviceModel> 
</configuration>
 WCFSolutionExtensions.config
XML
Edit|Remove
<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <system.serviceModel> 
        <extensions> 
            <behaviorExtensions> 
                <add name="bizTalkMessageInspector" type="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.MessageInspector.ServiceBusMessageInspectorBehaviorExtensionElement, Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.MessageInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=197ec3eb961f773c"/> 
                <add name="bizTalkListenUri" type="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.ListenUri.ListenUriBehaviorExtensionElement, Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.ListenUri, Version=1.0.0.0, Culture=neutral, PublicKeyToken=197ec3eb961f773c"/> 
                <add name="bizTalkSessionChannel" type="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.SessionChannel.SessionChannelBehaviorExtensionElement, Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.SessionChannel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=197ec3eb961f773c"/> 
                <add name="bizTalkSecurity" type="Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Security.TokenProviderBehaviorExtensionElement, Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Security, Version=1.0.0.0, Culture=neutral, PublicKeyToken=197ec3eb961f773c"/> 
            </behaviorExtensions> 
        </extensions> 
    </system.serviceModel> 
</configuration>
 
To configure BizTalk Server to use the Microsoft.ServiceBus.dll v.1.8 you can proceed as follows:
AssemblyBindingRedirect.xml
XML
Edit|Remove
<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <runtime> 
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
            <dependentAssembly> 
                <assemblyIdentity name="Microsoft.ServiceBus" publicKeyToken="31bf3856ad364e35" culture="Neutral" /> 
                <bindingRedirect oldVersion="1.7.0.0" newVersion="1.8.0.0" /> 
            </dependentAssembly> 
        </assemblyBinding> 
    </runtime> 
</configuration>
 

Receive Locations

We have now all the elements we need to create and configure the artifacts that compose our BizTalk application. In this section we will describe the steps necessary to create the WCF-Custom receive location used by the ServiceBusSample application to read messages from the requestqueue, then we’ll see how to define a WCF-Custom receive location to retrieve messages from the ItalyMilan subscription defined on the requesttopic.

Let’s start by looking at the steps necessary to create the WCF receive location used to read request messages from the requestqueue.

 

 Right-click the newly created receive port and select New Receive Location from the context menu.

 

 

 

 

 

Let’s now go through the steps to configure the WCF receive location used to read request messages from the ItalyMilan subscription of the requesttopic.

 

 

 

The following picture shows the two receive locations we just created along with two other request-response receive locations used that use the BasicHttpBinding and the NetTcpRelayBinding. As we said in the Scenarios section, the description of these two receive locations is out of the scope of this article.

Send Ports

In this section we will describe the steps necessary to create the WCF-Custom send ports used by the StaticSendPortOrchestration and DynamicSendPortOrchestration to send response messages to the requestqueue and requesttopic. In particular, the StaticSendPortOrchestration uses two static one-way send ports, one for each messaging entity, while the DynamicSendPortOrchestration uses a dynamic send port to decide at runtime, based on the address specified by the client application and contained in the ReplyTo context property, where to send the response message.

Let’s start by looking at the steps necessary to create the WCF send port used by the StaticSendPortOrchestration to send response messages to the responsequeue.

  • Open the BizTalk Server Administration Console and expand the Send Ports node of the ServiceBusForWindowsServer application.
  • Create a new Static One-way Send Port as shown in the figure below. Specify a name for the send port and choose a pipeline based on your requirements. In this demo, we will use the PassThruTransmit pipeline for performance reasons. Then choose the WCF-Custom adapter and click the Configure button.

 

 

 

 

Now, let’s go through the steps to create the WCF send port used by the StaticSendPortOrchestration to send response messages to the responsetopic.

 

 

 

 

As the last step, let’s create the dynamic send port used by the DynamicSendPortOrchestration to send messages to the responsequeue or responsetopic, based on the URL specified by the client application in the ReplyTo property of the Service Bus message.

 

The following picture shows the three send ports we created in the BizTalk Server Administration Console.

Orchestrations

In this section we will examine the structure of the StaticSendPortOrchestration and DynamicSendPortOrchestration and the code of the helper classes used by them.

This section covers the structure of the orchestrations and the code of the components they use. In particular, with the DynamicSendPortOrchestration, we’ll examine how to implement an asynchronous, request-response pattern where the client application dynamically indicates to the BizTalk application the address of the queue or topic where to send the response message.

StaticSendPortOrchestration

The following figure shows the structure of the StaticSendPortOrchestration.

 

The BusinessLogic expression shape at point 1 contains the code in the box below. The orchestration invokes the ProcessRequestReturnStream method on a RequestManager object that returns the payload of the response message as a stream. I will omit for brevity the description of the RequestManager helper component as the analysis of its code is unnecessary for the understanding of the operating mode of the solution.

 

 

C#
Edit|Remove
logHelper.WriteLine("[StaticSendPortOrchestration] Request message received from [{0}].", requestMessage(WCF.To)); 
stream = requestManager.ProcessRequestReturnStream(requestMessage); 
logHelper.WriteLine("[StaticSendPortOrchestration] Request message successfully processed.");
The MessageAssignment shape at point 2 creates the response message.
C#
Edit|Remove
responseMessage = null; 
responseManager.SetResponse(responseMessage, stream); 
responseMessage(*) = requestMessage(*); 
guid = System.Guid.NewGuid(); 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Schemas.Application) = "ServiceBusSample"; 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.PropertySchemas.MessageId) = guid.ToString(); 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.PropertySchemas.CorrelationId) = responseManager.GetMessageId(requestMessage); 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.PropertySchemas.SessionId) = responseManager.GetReplyToSessionId(requestMessage);

 

In this shape the orchestration performs the following actions:

The queue rule of the code snippet at point 3 invokes the ReturnResponseViaQueue method of the RequestManager object, which returns true if the value of the ReplyTo context property contains the word “queue” or false otherwise. In other words, the orchestration checks the value of the ReplyTo context property that contains the address specified by the client in the ReplyTo property of the BrokeredMessage and chooses whether to send the response message to the responsequeue or responsetopic based on the client demand.

DynamicSendPortOrchestration

The following figure shows the structure of the DynamicSendPortOrchestration.

 

The BusinessLogic expression shape  at point 1 contains the code in the box below. The orchestration invokes the ProcessRequestReturnStream method on a RequestManager object that returns the payload of the response message as a stream. I will omit for brevity the description of the RequestManager helper component as the analysis of its code is unnecessary for the understanding of the operating mode of the solution.

 

 

C#
Edit|Remove
logHelper.WriteLine("[DynamicSendPortOrchestration] Request message received from [{0}].", requestMessage(WCF.To)); 
stream = requestManager.ProcessRequestReturnStream(requestMessage); 
logHelper.WriteLine("[DynamicSendPortOrchestration] Request message successfully processed."); 
The MessageAssignment shape at point 2 creates the response message.
C#
Edit|Remove
responseMessage = null; 
responseManager.SetResponse(responseMessage, stream); 
responseMessage(*) = requestMessage(*); 
guid = System.Guid.NewGuid(); 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.Schemas.Application) = "ServiceBusSample"; 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.PropertySchemas.MessageId) = guid.ToString(); 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.PropertySchemas.CorrelationId) = responseManager.GetMessageId(requestMessage); 
responseMessage(Microsoft.WindowsAzure.CAT.Samples.ServiceBusForWindowsServer.PropertySchemas.SessionId) = responseManager.GetReplyToSessionId(requestMessage); 
responseMessage(WCF.InboundHeaders) = ""; 
responseMessage(WCF.Action)= responseManager.GetAction(requestMessage); 
responseMessage(WCF.BindingType)="netMessagingBinding"; 
responseMessage(WCF.EnableTransaction) = false; 
responseMessage(WCF.BindingConfiguration) = responseManager.GetBindingConfiguration(requestMessage); 
responseMessage(WCF.EndpointBehaviorConfiguration) = responseManager.GetEndpointBehaviorConfiguration(requestMessage); 
OneWayResponsePort(Microsoft.XLANGs.BaseTypes.Address) = responseManager.GetReplyTo(requestMessage); 
OneWayResponsePort(Microsoft.XLANGs.BaseTypes.TransportType)="WCF-Custom";

 

in this shape the orchestration performs the following actions:

For your convenience, I included the code of the ResponseManager class in the example below.

 

C#
Edit|Remove
/// <summary> 
/// This class contains helper methods used by the orchestrations. 
/// </summary> 
[Serializable] 
public class ResponseManager 
{ 
    #region Private Fields 
    //*************************** 
    // Private Fields 
    //*************************** 
    private readonly LogHelper logHelper = new LogHelper(); 
    #endregion 
 
    #region Public Methods 
    //*************************** 
    // Public Methods 
    //*************************** 
 
    /// <summary> 
    /// Sets the content of the XLANGMessage passed as first parameter. 
    /// </summary> 
    /// <param name="message">An XLANGMessage object.</param> 
    /// <param name="stream">The stream containing the payload to assign to the message.</param> 
    public void SetResponse(XLANGMessage message, Stream stream) 
    { 
        try 
        { 
            if (stream != null && 
                message != null && 
                message.Count > 0) 
            { 
                if (stream.CanSeek) 
                { 
                    stream.Seek(0, SeekOrigin.Begin); 
                } 
                message[0].LoadFrom(stream); 
            } 
        } 
        catch (Exception ex) 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] {0}", ex.Message)); 
        } 
        finally 
        { 
            if (message != null) 
            { 
                message.Dispose(); 
            } 
        } 
    }  
    /// <summary> 
    /// Gets the action for the response request. 
    /// </summary> 
    /// <param name="request">The request request.</param> 
    /// <returns>The action to assign to the Action header of the response request.</returns> 
    public string GetAction(XLANGMessage request) 
    { 
        const string action = "ReceiveResponse"; 
 
        try 
        { 
            // In a real application, the action should be retrieved from a 
            // configuration repository based on the request request content and context 
            // information. In addition, this data should be cached to improve performance. 
            return action; 
        } 
        catch (Exception ex) 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] {0}", ex.Message)); 
        } 
        finally 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] Action=[{0}]", action ?? "NULL")); 
            if (request != null) 
            { 
                request.Dispose(); 
            } 
        } 
        // Return default Action. 
        return action; 
    } 
 
    /// <summary> 
    /// Gets the binding configuration for the response request. 
    /// </summary> 
    /// <param name="request">The request request.</param> 
    /// <returns>The binding configuration of the dynamic send port used to send 
    /// out the response request.</returns> 
    public string GetBindingConfiguration(XLANGMessage request) 
    { 
        const string bindingConfiguration = @"<binding name=""netMessagingBinding"" " + 
                                            @"sendTimeout=""00:03:00"" " + 
                                            @"receiveTimeout=""00:03:00"" " + 
                                            @"openTimeout=""00:03:00"" " + 
                                            @"closeTimeout=""00:03:00"" " + 
                                            @"sessionIdleTimeout=""00:01:00"" " + 
                                            @"prefetchCount=""-1""> " + 
                                            @"<transportSettings batchFlushInterval=""00:00:01"" enableRedirect=""True""/>" + 
                                            @"</binding>"; 
        try 
        { 
            // In a real application, the binding configuration should be retrieved from a 
            // configuration repository based on the request request content and context 
            // information. In addition, this data should be cached to improve performance. 
            return bindingConfiguration; 
        } 
        catch (Exception ex) 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] {0}", ex.Message)); 
        } 
        finally 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] BindingConfiguration=[{0}]", bindingConfiguration ?? "NULL")); 
            if (request != null) 
            { 
                request.Dispose(); 
            } 
        } 
        // Return default binding configuration. 
        return bindingConfiguration; 
    } 
 
    /// <summary> 
    /// Gets endpoint behavior configuration of the dynamic  
    /// send port used to send out the response request. 
    /// </summary> 
    /// <param name="request">The request request.</param> 
    /// <returns>The endpoint behavior configuration of the dynamic  
    /// send port used to send out the response request.</returns> 
    public string GetEndpointBehaviorConfiguration(XLANGMessage request) 
    { 
        var endpointBehaviorConfiguration = @"<behavior name=""EndpointBehavior"">" + 
                                            @"<transportClientEndpointBehavior>" + 
                                            @"<tokenProvider>" + 
                                            @"<windowsAuthentication>" + 
                                            @"<stsUris>" + 
                                            "<stsUri value=\"https://" + Dns.GetHostEntry(string.Empty).HostName + ":9355/ServiceBusDefaultNamespace\" />" + 
                                            @"</stsUris>" + 
                                            @"</windowsAuthentication>" + 
                                            @"</tokenProvider>" + 
                                            @"</transportClientEndpointBehavior>" + 
                                            @"<serviceBusMessageInspector>" + 
                                            @"<propertiesToSend name=""Application,Country,City"" " + 
                                            @"namespace=""http://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver/propertyschema," +  
                                            @"http://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver/propertyschema," + 
                                            @"http://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver/propertyschema"" />" + 
                                            @"</serviceBusMessageInspector>" + 
                                            @"</behavior>"; 
        try 
        { 
            // In a real application, the endpoint behavior configuration should be retrieved from a 
            // configuration repository based on the request request content and context 
            // information. In addition, this data should be cached to improve performance. 
            return endpointBehaviorConfiguration; 
        } 
        catch (Exception ex) 
        {               
            logHelper.WriteLine(string.Format("[ResponseManager] {0}", ex.Message)); 
        } 
        finally 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] EndpointBehaviorConfiguration=[{0}]", endpointBehaviorConfiguration ?? "NULL")); 
            if (request != null) 
            { 
                request.Dispose(); 
            } 
        } 
        // Return default endpoint behavior configuration. 
        return string.Empty; 
    } 
 
    /// <summary> 
    /// Gets the value of the MessageId context property. 
    /// </summary> 
    /// <param name="request">The request request.</param> 
    /// <returns>The value of the MessageId context property.</returns> 
    public string GetMessageId(XLANGMessage request) 
    { 
        try 
        { 
            if (request != null) 
            { 
                var messageId = request.GetPropertyValue(typeof(MessageId)) as string; 
                if (!string.IsNullOrEmpty(messageId)) 
                { 
                    logHelper.WriteLine(string.Format("[ResponseManager] MessageId=[{0}]", messageId)); 
                    return messageId; 
                } 
            } 
        } 
        catch (Exception ex) 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] Exception: {0}", ex.Message)); 
        } 
        finally 
        { 
            if (request != null) 
            { 
                request.Dispose(); 
            } 
        } 
        return string.Empty; 
    } 
 
    /// <summary> 
    /// Gets the value of the ReplyTo context property. 
    /// </summary> 
    /// <param name="request">The request request.</param> 
    /// <returns>The value of the ReplyTo context property.</returns> 
    public string GetReplyTo(XLANGMessage request) 
    { 
        try 
        { 
            if (request != null) 
            { 
                var replyTo = request.GetPropertyValue(typeof(ReplyTo)) as string; 
                if (!string.IsNullOrEmpty(replyTo)) 
                { 
                    logHelper.WriteLine(string.Format("[ResponseManager] ReplyTo=[{0}]", replyTo)); 
                    return replyTo; 
                } 
            } 
        } 
        catch (Exception ex) 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] Exception: {0}", ex.Message)); 
        } 
        finally 
        { 
            if (request != null) 
            { 
                request.Dispose(); 
            } 
        } 
        return string.Empty; 
    } 
 
    /// <summary> 
    /// Gets the value of the ReplyToSessionId context property. 
    /// </summary> 
    /// <param name="request">The request request.</param> 
    /// <returns>The value of the ReplyToSessionId context property.</returns> 
    public string GetReplyToSessionId(XLANGMessage request) 
    { 
        try 
        { 
            if (request != null) 
            { 
                var replyToSessionId = request.GetPropertyValue(typeof(ReplyToSessionId)) as string; 
                if (!string.IsNullOrEmpty(replyToSessionId)) 
                { 
                    logHelper.WriteLine(string.Format("[ResponseManager] ReplyToSessionId=[{0}]", replyToSessionId)); 
                    return replyToSessionId; 
                } 
                return !string.IsNullOrEmpty(replyToSessionId) ? replyToSessionId : string.Empty; 
            } 
        } 
        catch (Exception ex) 
        { 
            logHelper.WriteLine(string.Format("[ResponseManager] Exception: {0}", ex.Message)); 
        } 
        finally 
        { 
            if (request != null) 
            { 
                request.Dispose(); 
            } 
        } 
        return string.Empty; 
    } 
    #endregion 
}

Testing the Solution

Once you have properly deployed and configured the solution, you can use the Service Bus Explorer or the the client application included in the solution to test the application.

Service Bus Explorer

In order to test the application using the Service Bus Explorer, you can proceed as follows:

 

Client Application

In order to test the application using the client application, you can proceed as follows:

The figure below shows the most interesting combination:

 

The client sends the request message to requesttopic.

Looking at the log of the client application , you can note the following:

Conclusions

This solution demostrates how you can integrate BizTalk Server 2010 / 2013 with the Service Bus for Windows Server by using the WCF-Custom adapter, the NetMessagingBinding and a few WCF extensions. In the future, the SB-Messaging adapter will likely support the integration with the on-premises version of the Service Bus. When this will happen, this solution won't be necessary to integrate BizTalk Server 2013 with the Service Bus for Windows Server, but it will continue to be valid for BizTalk Server 2010.