Share via


Step 5: Create the Orchestrations

The next step to examine the StaticSendPortOrchestration and DynamicSendPortOrchestration structure and the code of the helper classes used by the orchestrations.

This topic covers the orchestration structure and the component code used by the orchestrations. The DynamicSendPortOrchestration shows how to implement an asynchronous, request-response pattern where the client application dynamically specifies the queue or topic address to send the response message.

In This Section:

  • StaticSendPortOrchestration

  • DynamicSendPortOrchestration

StaticSendPortOrchestration

The following image shows the StaticSendPortOrchestration structure:

StaticSendPortOrchestration With Numbers

At Point 1, the BusinessLogic expression shape contains the following code. The orchestration invokes the ProcessRequestReturnStream method on a RequestManager object that returns the payload of the response message as a stream:

Note

For brevity, the RequestManager helper component description is omitted because its code is unnecessary for the understanding of the solution.

logHelper.WriteLine("[StaticSendPortOrchestration] Request message received from [{0}].", requestMessage(WCF.To));
stream = requestManager.ProcessRequestReturnStream(requestMessage);
logHelper.WriteLine("[StaticSendPortOrchestration] Request message successfully processed.");

At Point 2, the MessageAssignment shape creates the response message:

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 the MessageAssignment shape, the orchestration performs the following actions:

  1. Invokes the SetResponse method on a ResponseManager object to assign the stream that contains the payload to the response message.

  2. Copies the context properties from the request to the response message.

  3. Assigns a value to the Application context property. Using the Application context property demonstrates how a BizTalk application can send context information out-of-band to another application through a Service Bus message. In Step 4: Configure the Send Ports, the static send ports are configured to translate the Application context property into a user-defined property of the outgoing BrokeredMessage.

  4. Creates a MessageId for the response message.

  5. Copies the MessageId from the request message to the CorrelationId of the response message. This copy allows the client to correlate the response message with the initial request.

  6. Copies the ReplyToSessionId from the request message to the SessionId of the response message.

    Note

    Using sessions is necessary when multiple consumers use the same sessionful queue or subscription to receive response messages. In this case, a publisher application can use the ReplyToSessionId property of a BrokeredMessage to communicate to the consumer the value to assign to the SessionId of response messages.

At Point 3, the Queue rule of the code snippet invokes the ReturnResponseViaQueue method of the RequestManager object; which returns true if the value of the ReplyTo context property contains the word “queue”. Otherwise, it returns false. 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. It chooses whether to send the response message to the responsequeue or responsetopic, based on the client demand.

DynamicSendPortOrchestration

The following image shows the DynamicSendPortOrchestration structure:

DynamicSendPortOrchestration With Numbers

At Point 1, the BusinessLogic expression shape contains the following code. The orchestration invokes the ProcessRequestReturnStream method on a RequestManager object that returns the payload of the response message as a stream:

Note

For brevity, the RequestManager helper component description is omitted because its code is unnecessary for the understanding of the solution.

logHelper.WriteLine("[DynamicSendPortOrchestration] Request message received from [{0}].", requestMessage(WCF.To));
stream = requestManager.ProcessRequestReturnStream(requestMessage);
logHelper.WriteLine("[DynamicSendPortOrchestration] Request message successfully processed.");

At Point 2, the MessageAssignment shape creates the response message:

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 the MessageAssignment shape, the orchestration performs the following actions:

  1. Invokes the SetResponse method on a ResponseManager object to assign the stream that contains the payload to the response message.

  2. Copies the context properties from the request to the response message.

  3. Assigns a value to the Application context property. Using the Application context property demonstrates how a BizTalk application can send context information out-of-band to another application through a Service Bus message. In Step 4: Configure the Send Ports, the static send ports are configured to translate the Application context property into a user-defined property of the outgoing BrokeredMessage.

  4. Creates a MessageId for the response message.

  5. Copies the MessageId from the request message to the CorrelationId of the response message. This copy allows the client to correlate the response message with the initial request.

The DynamicSendPortOrchestration orchestration performs the following actions:

  1. Copies the ReplyToSessionId from the request message to the SessionId of the response message.

    Note

    Using sessions is necessary when multiple consumers use the same sessionful queue or subscription to receive response messages. In this case, a publisher application can use the ReplyToSessionId property of a BrokeredMessage to communicate to the consumer the value to assign to the SessionId of response messages.

  2. Invokes the GetAction method on the ResponseManager object to get the WCF.Action value.

  3. Specifies the NetMessagingBinding as WCF.BindingType for the dynamic send port.

  4. Assigns false to the WCF.EnableTransaction property.

  5. Invokes the GetBindingConfiguration method on the ResponseManager object to get the value for the WCF.BindingConfiguration.

  6. Invokes the GetEndpointBehaviorConfiguration method on the ResponseManager object to get the value for the WCF.EndpointBehaviorConfiguration.

  7. Invokes the GetReplyTo method on the ResponseManager object to get the address of the dynamic send port.

  8. Sets the TransportType of the dynamic send port to WCF-Custom.

ResponseManager class code:

/// <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=""https://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver/propertyschema," + 
                                            @"https://windowsazure.cat.microsoft.com/samples/servicebusforwindowsserver/propertyschema," +
                                            @"https://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
}

Next

Invoke the application

See Also

Concepts

Step 1: Create the Property Schemas
Step 2: Create the Request and Response Schemas
Step 3: Configure the Receive Locations
Step 4: Configure the Send Ports
Step 5: Create the Orchestrations
Create the BizTalk artifacts