Create the Client Application

To submit messages to the BizTalk Server application, you can use Service Bus Explorer or you can use the Client Application included in the solution. This topic lists the steps to create the Client Application. If you are using Service Bus Explorer to submit messages, skip this section and go to Test the Solution.

Client Application

The Client Application is a Windows Form application that exchanges messages with the BizTalk application asynchronously through Service Bus messaging entities. The Client Application acts as a client and a service application at the same time. This 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.

The Client Application uses the following configuration file to define the WCF client and service endpoints that communicate with the Service Bus:

<?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.ConnectionStatusElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="transportClientEndpointBehavior" type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="serviceRegistrySettings" type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.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.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="tcpRelayTransport" type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="httpRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="httpsRelayTransport" type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="onewayRelayTransport" type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingElementExtensions>
      <bindingExtensions>
        <add name="basicHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="webHttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="ws2007HttpRelayBinding" type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netTcpRelayBinding" type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netOnewayRelayBinding" type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netEventRelayBinding" type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </bindingExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

The following list describes the main elements and sections of this configuration file:

system.diagnostics

Defines a custom listener called LogTraceListener. The ResponseHandlerService uses LogTraceListener to write the response message to the log control of this Windows Forms application.

securityBehavior

Defines the token provider used by the client and service endpoints to authenticate against the local STS of the Service Bus farm.

requestQueueClientEndpoint

Used by the application to send request messages to the requestqueue.

requestTopicClientEndpoint

Used by the application to send request messages to the requesttopic.

responseQueueServiceEndpoint

Used by the application to receive response messages from the responsequeue.

responseSubscriptionServiceEndpoint

Used by the application to send receive response messages from the ItalyMilan subscription for the responsetopic.

Note

When you configure a WCF service endpoint to consume messages from a sessionful queue or subscription, the service contract needs to support sessions. When you configure the responseQueueServiceEndpoint or responseSubscriptionServiceEndpoint endpoints to receive from a sessionful queue and subscription, replace the ICalculatorResponse service contract with the sessionful ICalculatorResponseSessionful contract interface.

The following example shows the client application code that starts the ResponseHandlerService WCF service. The ResponseHandlerService WCF service reads response messages from the responsequeue and the ItalyMilan subscription of the responsetopic. The ResponseHandlerService WCF service code is detailed in Create the Response Handler Service.

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 image shows the user interface of the client application:

Client Application

Using the radio buttons in the Request Method group, you can send the request message to the requestqueue or the requesttopic. Using the radio buttons in the Response Method group, you can receive the response from the responsequeue or from the ItalyMilan subscription of the responsetopic. To communicate your choice to the BizTalk application, the Client Application uses a BrokeredMessageProperty object to assign the value of the responseQueueUri or responseTopicUri private fields to the ReplyTo property. The Client Application uses the following code:

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;
    }
}

Next

Create the Response Handler Service

See Also

Concepts

Create the Client Application
Create the Response Handler Service
Invoke the application