How using Duplex MEP to communicate with BizTalk from a .NET application or a WF workflow running inside AppFabric Part 1

Introduction

Some time ago, I was asked by a customer whether or not BizTalk WCF Adapters support the Duplex Message Exchange Pattern. The latter is characterized by the ability of both the service and the client to send messages to each other independently either using one-way or request/reply messaging. This form of bi-directional communication is useful for services that must communicate directly to the client or for providing an asynchronous experience to either side of a message exchange, including event-like behavior. The BizTalk WCF Adapters support this message exchange pattern, but unfortunately this feature is undocumented. Therefore, I decided to create a full demo where a client .NET application exchange messages with an Orchestration via a two-way Request-Response WCF Receive Location that supports callbacks and duplex communication. Subsequently, I decided to extend this demo and introduce a WCF Workflow Service between the WinForm client application and the underlying Orchestration to implement a more complex scenario The final objective was to investigate how exploiting the Correlation mechanisms provided by WF 4.0 to get a workflow running within IIS/AppFabric to exchange messages with a downstream orchestration in an asynchronous mode.

This article will be composed of 3 parts:

Why Duplex Message Exchange Pattern?

The Duplex Message Exchange Pattern is fully supported by WCF and is extensively documented on MSDN. As I already mentioned in the introduction, the Duplex pattern allows two applications to act both as service endpoints and send messages to each other independently in an asynchronous way. There are many reasons for using the Duplex Message Exchange Pattern when communicating with a BizTalk solution:

  • If an orchestration invoked by the consumer application takes seconds to minutes to complete.
  • If you are invoking an orchestration within an ASP.NET page, use asynchronous pages.
  • If you are invoking an orchestration from a single threaded application, such as a Windows Forms or Windows Presentation Foundation (WPF) application. When using the event-based asynchronous calling model, the result event is raised on the UI thread, adding responsiveness to the application without requiring you to handle multiple threads yourself.

In general, if you have a choice between a synchronous and asynchronous call, you should choose the latter approach. In fact, a synchronous call typically blocks the client thread till the operation completes, whereas an asynchronous call is non-blocking and only initiates the operation. This way the consumer application can continue its execution without waiting for the call to complete and get notified by the service when the result of its request is ready.

For more information on how implementing the Duplex Message Exchange Pattern with WCF, you can read the following articles:

Using a Duplex Channel to communicate with a BizTalk Application

The following picture represents the architecture of the first use case. The idea behind the application is quite straightforward: a Windows Forms application submits a question to an orchestration called SyncMagic8Ball via a WCF-NetTcp Receive Location. The orchestration is a BizTalk version of the notorious Magic 8 Ball toy and it randomly returns one of 20 standardized answers. 

Message Flow
  1. The  Windows Forms Client Application enables a user to specify a question and a delay in seconds. When the user presses the Ask button, a new request message containing the question and the delay is created and sent to a Two-Way WCF-NetTcp Receive Location.
  2. The XmlReceive pipeline promotes the MessageType context property.
  3. The Message Agent submits the request message to the MessageBox (BizTalkMsgBoxDb).
  4. A new instance of the SyncMagic8Ball orchestration receives the request message via a two-way logical port and uses a custom helper component called XPathHelper to read the value of the Question and Delay elements from the inbound message.
  5. The SyncMagic8Ball orchestration invokes the SetResponse static method exposed by the ResponseHelper class to build the response message containing the answer to this question contained in the request message. The response message is then published to the MessageBox (BizTalkMsgBoxDb) by the Message Agent.
  6. The response message is retrieved by the WCF-NetTcp Receive Location.
  7. The PassThruTransmit send pipeline is executed by the WCF-NetTcp Receive Location.
  8. The response message is finally returned to the original caller.

WCF Receive Locations

In order to establish an asynchronous communication, the client application and service, in our case BizTalk Server, have to meet the following conditions:

  1. They need to be online simultaneously.
  2. They need to use the same WCF binding.
  3. The selected binding must support sessions and duplex contracts. WCF provides a rich set of built-in bindings that support the Duplex MEP. For more information, you can read “System-Provided Bindings” topic on MSDN.
  4. The client application needs to implement a Service Contract to submit the request message to BizTalk and expose a Callback Contract to receive the response message from BizTalk Server asynchronously.

To satisfy the second and third conditions, I have created 3 different Two-Way Request-Response Receive Locations with the following characteristics:

  • The 3 Receive Locations are children of the same Receive Port called DuplexMEP.Sync.ReceivePort.
  • They use the XmlReceive pipeline to process the inbound message and the PassThruTrasmit pipeline to process the outbound message.
  • The DuplexMEP.Sync.WCF-NetTcp.ReceiveLocation uses the WCF-NetTcp Adapter and is hosted by the BizTalkServerReceiveHost64 in-process host.
  • The DuplexMEP.Sync.WCF-NetNamedPipe.ReceiveLocation uses the WCF-NetNamedPipe Adapter and is hosted by the BizTalkServerReceiveHost64 in-process host.
  • The DuplexMEP.Sync.WCF-Custom.WsDualHttpBinding.ReceiveLocation uses the WCF-CustomIsolated Adapter in conjunction with the WsDualHttpBinding and is hosted by the BizTalkServerIsolatedHost.

The following picture shows the 3 Receive Locations inside the BizTalk Administration Console:

 Note
You should select the most appropriate WCF Adapter and binding based on your needs:

  • The WsDualHttpBinding binding is designed for interoperability with advanced web services that need to support WS-* standards. It uses the HTTP protocol for the transport and the text message encoding.
  • NetTcpBinding and NetNamedPipeBinding, on the other hand, are designed for efficient and performant communication with other WCF applications across machines or on the same machine respectively. They both use the binary message encoding that guarantees better performance than the text message encoding.

Bindings have different characteristics in terms of response time and throughput, so the general advice to increase performance is using the NetTcpBinding and NetNamedPipeBinding whenever possible.

Schemas

The request messages sent by the Windows Forms Client Application have the following format:

Request Message

<Request xmlns="http://microsoft.appfabric.cat/10/samples/duplexmep">
    <Id>9aff8596-ec87-494d-801e-30f286d449a4</Id>
    <Question>Will the world end in 2012?</Question>
    <Delay>0</Delay>
</Request>

The corresponding response messages returned by the SyncMagic8Ball orchestration have the following format:

Response Message

<Response xmlns="http://microsoft.appfabric.cat/10/samples/duplexmep">
    <Id>9aff8596-ec87-494d-801e-30f286d449a4</Id>
    <Answer>Most likely</Answer>
</Response>

Both message types are defined by an XML Schema contained in the Schemas project that you can find in the companion code for this article.

Orchestration

The following picture shows the structure of the SyncMagic8Ball orchestration.

As you can easily notice, the orchestration uses the Two-Way Request-Response Logical Port to receive the inbound request message and to return the corresponding response message. The Trace Request Expression Shape contains the following code to extract the information from the request message. The namespace of the LogHelper and XPathHelper static classes have been eliminated for ease of reading.

LogHelper.WriteLine(System.String.Format("[SyncMagic8Ball] Transport: {0}", 
                                         RequestMessage(BTS.InboundTransportType))); id = XPathHelper.GetValue(RequestMessage, 0, "Id Element XPath Expression"); if (!System.Int32.TryParse(XPathHelper.GetValue(RequestMessage, 0, "Delay Element XPath Expression"),
                           out delayInSeconds)) { delayInSeconds = 0; } LogHelper.WriteLine(System.String.Format("[SyncMagic8Ball] Id: {0}", id)); LogHelper.WriteLine(System.String.Format("[SyncMagic8Ball] Question: {0}",
                                         XPathHelper.GetValue(RequestMessage, 0,
                                                              "Question Element XPath Expression"))); LogHelper.WriteLine(System.String.Format("[SyncMagic8Ball] Delay: {0}", delayInSeconds));

You can use DebugView to monitor the trace produced by the orchestration and helper components.

 Note 
My LogHelper class traces messages to the standard output using the capability supplied by the Trace class. This component is primarily intended to be used for debugging a BizTalk application in a test environment, rather than to be used in a production environment. If you are looking for a tracing framework which combines the high performance and flexibility provided by the Event Tracing for Windows (ETW) infrastructure, you can review the following articles by Valery Mizonov:

  • Streamlining Custom Pipeline Component Development Using Base Class Library.
  • Best Practices for Instrumenting High Performance BizTalk Solutions.
  • How to Support Component-Level Instrumentation Using BizTalk CAT Instrumentation Framework.
  • Management Tasks for BizTalk CAT Instrumentation Framework – Fully Explained.

The value of the Delay Shape is defined as follows:

new System.TimeSpan(0, 0, delayInSeconds);

Therefore, the orchestration waits for the time interval in seconds specified in the request message before returning the response message to the caller.

 Note

To extract the value of individual fields from the inbound document, I could have used Distinguished Fields defined on the XML Schema for the request message. These are context properties whose name is defined by the XPath Expression used to retrieve data from an input XML document. If the XML Schema of the document is quite complex, the XPath Expression in question can be very long and therefore the corresponding Distinguished Field can occupy a significant amount of space in the message context. Therefore, I recommend to implement the following best practices in any BizTalk application: 

  • Eliminate unnecessary and unused Distinguished Fields.
  • Use a different technique to access the value of individual elements and attributes within an Xml document.

When the document in question is extremely small, you can also load the message into an XmlDocument object and use the SelectSingleNode method to extract the information you need using an XPath Expression. However, the general advice is to minimize the usage of XmlDocument variables in orchestrations and in .NET code. Loading a message into an XmlDocument variable has significant overhead, especially for large messages.  This overhead is in terms of memory usage and system resources required to build the in-memory structures.  The use of an XmlDocument instance forces the message content to be entirely loaded into memory in order to build the object graph for the DOM.  The total amount of memory used by an instance of this class can be around 10 times the actual message size. For more information and evidence, see the following articles I wrote on this topic:

  • ”4 Different ways to process an XLANGMessage within an helper component invoked by an orchestration Part 1” on the AppFabric CAT blog.
  • ”4 Different ways to process an XLANGMessage within an helper component invoked by an orchestration Part 2” on the AppFabric CAT blog.

The request message in my demo is relatively small; nevertheless,  I decided to use an helper class that adopts a streaming approach to extract data from the XLANGMessage passed in as argument:

public class XPathHelper
{
    #region Private Constants 
    private const string MessageCannotBeNull = "[XPathReader] The message cannot be null.";
    #endregion
        
    #region Public Static Methods
    public static string GetValue(XLANGMessage message, int partIndex, string xpath)
    {
        try
        {
            if (message == null)
            {
                throw new ApplicationException(MessageCannotBeNull);
            }
            using (Stream stream = message[partIndex].RetrieveAs(typeof(Stream)) as Stream)
            {
                XmlTextReader xmlTextReader = new XmlTextReader(stream);
                XPathCollection xPathCollection = new XPathCollection();
                XPathReader xPathReader = new XPathReader(xmlTextReader, xPathCollection);
                xPathCollection.Add(xpath);
                bool ok = false;
                while (xPathReader.Read() && !ok)
                {
                    if (xPathReader.HasAttributes)
                    {
                        for (int i = 0; i < xPathReader.AttributeCount; i++)
                        {
                            xPathReader.MoveToAttribute(i);
                            if (xPathReader.Match(xPathCollection[0]))
                            {
                                return xPathReader.GetAttribute(i);
                            }
                        }
                    }
                    if (xPathReader.Match(xPathCollection[0]) && !ok)
                    {
                        return xPathReader.ReadString();
                    }
                }
            }
        }
        finally
        {
            message.Dispose();
        }
        return string.Empty;
    }
    #endregion
}

The Set Response Message Assignment shape invokes the SetResponse static method exposed by the ResponseHelper class to generate the response message:

ResponseHelper Class

public class ResponseHelper
{
    #region Private Constants
    private const string MessageFormat = "[ResponseManager] {0}";
    private const string ResponseNamespace = "http://microsoft.appfabric.cat/10/samples/duplexmep";
    private const string Response = "Response";
    private const string Id = "Id";
    private const string Answer = "Answer";
    #endregion

    #region Static Constructor
    private static string[] answers = null;
    #endregion

    #region Static Constructor
    static ResponseHelper()
    {
        answers = new string[]{"As I see it, yes",
                                "It is certain",
                                "It is decidedly so",
                                "Most likely",
                                "Outlook good",
                                "Signs point to yes",
                                "Without a doubt",
                                "Yes",
                                "Yes - definitely",
                                "You may rely on it",
                                "Reply hazy, try again",
                                "Ask again later",
                                "Better not tell you now",
                                "Cannot predict now",
                                "Concentrate and ask again",
                                "Don't count on it",
                                "My reply is no",
                                "My sources say no",
                                "Outlook not so good",
                                "Very doubtful"};
    }
    #endregion

    #region Public Static Methods
    public static void SetResponse(XLANGMessage message, string id)
    {
        try
        {
            VirtualStream stream = new VirtualStream();
            using (XmlWriter writer = XmlWriter.Create(stream))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement(Response, ResponseNamespace);
                writer.WriteStartElement(Id, ResponseNamespace);
                writer.WriteString(id);
                writer.WriteEndElement();
                writer.WriteStartElement(Answer, ResponseNamespace);
                Random random = new Random(unchecked((int)DateTime.Now.Ticks));
                writer.WriteString(answers[random.Next(0, 20)]);
                writer.WriteEndElement();
                writer.WriteEndElement();
            }
            stream.Seek(0, SeekOrigin.Begin);
            message[0].LoadFrom(stream);
        }
        catch (Exception ex)
        {
            LogHelper.WriteLine(ex.Message);
        }
        finally
        {
            message.Dispose();
        }
    }
    #endregion
}

As you can see, the ResponseHelper class uses a VirtualStream object an XmlWriter class instance to initialize the content of the response message. Once again, the response message in this case is so small that I could have used an XmlDocument object to load the content of the response message. However, my intention was to show you how to take advantage of the streaming approach to read and write the content of an XLANGMessage object within an orchestration.

Let’s take a look at the code used by the Windows Forms Client Application to send a request message and receive the corresponding response using a Callback contract.

Data and Message Contracts

I started defining the Data and Message Contracts for the request and response messages. These 2 types of contracts have different roles in WCF:

  • 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.
  • 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.

For more information on Data and Message Contracts, you can read the following articles:

In my solution, I created 2 separate projects called DataContracts and MessageContracts respectively. For your convenience, I included below the code of the 4 classes used to define the Data and Message Contract of the request and response messages.

BizTalkRequest Class

[DataContract(Name="Request", Namespace="http://microsoft.appfabric.cat/10/samples/duplexmep")]
public partial class BizTalkRequest : IExtensibleDataObject
{
    #region Private Fields
    private ExtensionDataObject extensionData;
    private string id;
    private string question;
    private int delay;
    #endregion

    #region Public Constructors
    public BizTalkRequest()
    {
        this.id = Guid.NewGuid().ToString();
        this.question = null;
        this.delay = 0;
    }

    public BizTalkRequest(string question, int delay)
    {
        this.id = Guid.NewGuid().ToString();
        this.question = question;
        this.delay = delay;
    }
    #endregion

    #region Public Properties
    public ExtensionDataObject ExtensionData
    {
        get
        {
            return this.extensionData;
        }
        set
        {
            this.extensionData = value;
        }
    }

    [DataMemberAttribute(IsRequired = true, Order = 1)]
    public string Id
    {
        get
        {
            return this.id;
        }
        set
        {
            this.id = value;
        }
    }

    [DataMemberAttribute(IsRequired = true, Order = 2)]
    public string Question
    {
        get
        {
            return this.question;
        }
        set
        {
            this.question = value;
        }
    }

    [DataMemberAttribute(IsRequired = true, Order = 3)]
    public int Delay
    {
        get
        {
            return this.delay;
        }
        set
        {
            this.delay = value;
        }
    } 
    #endregion
}

BizTalkResponse Class

[DataContract(Name = "Response", Namespace = "http://microsoft.appfabric.cat/10/samples/duplexmep")]
public partial class BizTalkResponse : IExtensibleDataObject
{
    #region Private Fields
    private ExtensionDataObject extensionData;
    private string id;
    private string answer;
    #endregion

    #region Public Properties
    public ExtensionDataObject ExtensionData
    {
        get
        {
            return this.extensionData;
        }
        set
        {
            this.extensionData = value;
        }
    }

    [DataMemberAttribute(IsRequired = true, Order = 1)]
    public string Id
    {
        get
        {
            return this.id;
        }
        set
        {
            this.id = value;
        }
    }

    [DataMemberAttribute(IsRequired = true, Order = 2)]
    public string Answer
    {
        get
        {
            return this.answer;
        }
        set
        {
            this.answer = value;
        }
    }
    #endregion
}

BizTalkRequestMessage Class

[MessageContract(IsWrapped=false)]
public class BizTalkRequestMessage
{
    #region Private Fields
    private BizTalkRequest request;
    #endregion

    #region Public Constructors
    public BizTalkRequestMessage()
    {
        this.request = null;
    }

    public BizTalkRequestMessage(BizTalkRequest request)
    {
        this.request = request;
    }
    #endregion

    #region Public Properties
    [MessageBodyMember(Namespace = "http://microsoft.appfabric.cat/10/samples/duplexmep")]
    public BizTalkRequest Request
    {
        get
        {
            return this.request;
        }
        set
        {
            this.request = value;
        }
    } 
    #endregion
}

BizTalkResponseMessage Class

[MessageContract(IsWrapped = false)]
public class BizTalkResponseMessage
{
    #region Private Fields
    private BizTalkResponse response;
    #endregion

    #region Public Constructors
    public BizTalkResponseMessage()
    {
        this.response = null;
    }

    public BizTalkResponseMessage(BizTalkResponse response)
    {
        this.response = response;
    }
    #endregion

    #region Public Properties
    [MessageBodyMember(Namespace = "http://microsoft.appfabric.cat/10/samples/duplexmep")]
    public BizTalkResponse Response
    {
        get
        {
            return this.response;
        }
        set
        {
            this.response = value;
        }
    }
    #endregion
}

Indeed, I could have used just Data Contracts to model messages as Message Contracts add a degree of complexity. So why I decided to use Message Contracts? Well, the reason is quite straightforward. 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. On the contrary, 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.

 Note 
The namespace of the Message and Data Contract classes match those defined by the XML Schemas that model the request and response messages in the BizTalk Server application.

Service Contracts

The next step was to define the Service Contracts used by the client application to exchange messages with BizTalk Server. To this purpose, I created a new project in my solution called ServiceContracts, and then I started off by defining the Service Contract Interface that models the server side of the duplex contract. I declared the signature of a One-Way method called AskQuestion. I specified a parameter of type BizTalkRequestMessage (see the Data and Message Contracts section) and void as return type. Then I decorated the method with the XmlSerializerFormatAttribute to indicate to the WCF Runtime to use the XmlSerializer instead of the DataContractSerializer. Please note that the WCF Adapters and BizTalk in general use the XmlSerializer, thus I had to explicitly set the correct serializer in the contract definition. Next, I decorated the method with the OperationContractAttribute to indicate that the method is one way and to specify the WS-Addressing Action of the request message. Finally I decorated the interface with the ServiceContractAttribute.

IMagic8BallBizTalk Interface

[ServiceContract(Namespace = http://microsoft.appfabric.cat/10/samples/duplexmep,
                 SessionMode = SessionMode.Required, CallbackContract = typeof(IMagic8BallBizTalkCallback), ConfigurationName = "IMagic8BallBizTalk")] public interface IMagic8BallBizTalk { [XmlSerializerFormat] [OperationContract(Action = "AskQuestion", IsOneWay = true)] void AskQuestion(BizTalkRequestMessage requestMessage); }

Then I defined the the callback interface as follows:

IMagic8BallBizTalkCallback Interface

[ServiceContract(Namespace = http://microsoft.appfabric.cat/10/samples/duplexmep, 
                 ConfigurationName = "IMagic8BallBizTalkCallback")] public interface IMagic8BallBizTalkCallback { [XmlSerializerFormat] [OperationContract(Action = "AskQuestionResponse", IsOneWay = true)] void AskQuestionResponse(BizTalkResponseMessage responseMessage); }

Finally, I linked the two interfaces into a Duplex Contract by assigning the type of the callback interface to the CallbackContract property in the ServiceContractAttribute that decorates the IMagic8BallBizTalk interface.

Callback Contract Implementation

The next step was to implement the IMagic8BallBizTalkCallback callback interface in order to receive response messages from BizTalk Server. To this purpose  I created a new class in the Client project called Magic8BallBizTalkCallback that implements the IMagic8BallBizTalkCallback callback interface.

Magic8BallBizTalkCallback Class

public class Magic8BallBizTalkCallback : IMagic8BallBizTalkCallback { #region Private Constants private const string ResponseFormat = "Response:\n\tId: {0}\n\tAnswer: {1}"; #endregion

 


#region Private Fields private MainForm form; #endregion #region Public Constructors public Magic8BallBizTalkCallback() { this.form = null; } public Magic8BallBizTalkCallback(MainForm form) { this.form = form; } #endregion #region IMagic8BallCallback Members public void AskQuestionResponse(BizTalkResponseMessage responseMessage) { if (responseMessage != null && responseMessage.Response != null && !string.IsNullOrEmpty(responseMessage.Response.Id) && !string.IsNullOrEmpty(responseMessage.Response.Answer)) { form.WriteToLog(string.Format(CultureInfo.CurrentCulture,
                                          ResponseFormat,
responseMessage.Response.Id,
responseMessage.Response.Answer)); } } #endregion } public partial class MainForm : Form { ... public void WriteToLog(string message) { if (InvokeRequired) { Invoke(new Action<string>(InternalWriteToLog), new object[] { message }); } else { InternalWriteToLog(message); } } ... }

In particular, when the WCF Receive Location invokes the AskQuestionResponse method to return a response message, the callback is handled by a ThreadPool thread other than the main thread running the client application. Since the InternalWriteToLog method is used to write the answer on a ListBox control owned by the main thread, the WriteToLog method exposed by the MainForm class uses the InvokeRequired property to check whether it’s necessary to use the Invoke method because the caller is on a different thread than the main thread.

Invoking BizTalk Server

The next step was to write the code to invoke one of the 3 WCF Receive Locations exposed by by BizTalk application.

btnAsk_Click Method

private void btnAsk_Click(object sender, EventArgs e)
{
    try
    {
        int delay = 0;
        if (string.IsNullOrEmpty(txtQuestion.Text))
        {
            WriteToLog(QuestionCannotBeNull);
            txtQuestion.Focus();
            return;
        }
        if (string.IsNullOrEmpty(txtDelay.Text) ||
            !int.TryParse(txtDelay.Text, out delay))
        {
            WriteToLog(DelayMustBeANumber);
            txtDelay.Focus();
            return;
        }

        if (string.IsNullOrEmpty(cboEndpoint.Text))
        {
            WriteToLog(NoEndpointsFound);
        }

        DuplexChannelFactory<IMagic8BallBizTalk> channelFactory = null;

        try
        {
            BizTalkRequest request = new BizTalkRequest(txtQuestion.Text, delay);
            BizTalkRequestMessage requestMessage = new BizTalkRequestMessage(request);
            WriteToLog(string.Format(CultureInfo.CurrentCulture, 
                                     RequestFormat,
                                     cboEndpoint.Text,
                                     request.Id,
                                     request.Question)); InstanceContext context = new InstanceContext(new Magic8BallBizTalkCallback(this)); channelFactory = new DuplexChannelFactory<IMagic8BallBizTalk>(context, cboEndpoint.Text); IMagic8BallBizTalk channel = channelFactory.CreateChannel(); channel.AskQuestion(requestMessage); } catch (FaultException ex) { WriteToLog(ex.Message); if (channelFactory != null) { channelFactory.Abort(); } } catch (CommunicationException ex) { WriteToLog(ex.Message); if (channelFactory != null) { channelFactory.Abort(); } } catch (TimeoutException ex) { WriteToLog(ex.Message); if (channelFactory != null) { channelFactory.Abort(); } } catch (Exception ex) { WriteToLog(ex.Message); if (channelFactory != null) { channelFactory.Abort(); } } } catch (Exception ex) { WriteToLog(ex.Message); } }

As you may have noticed, the method btnAsk_Click performs the following actions in order:

  • Creates a data contract object (BizTalkRequest)
  • Creates a message contract object (BizTalkRequestMessage).
  • Creates a new instance of the Magic8BallBizTalkCallback type to handle callbacks.
  • Creates a new InstanceContext object. In particular, the Magic8BallBizTalkCallback object is passed in as argument to the constructor of the InstanceContext object.
  • Instantiates a DuplexChannelFactory to create a duplex channel of type IMagic8BallBizTalk.  In particular, the first argument specifies the InstanceContext that the client uses to listen for messages from the connected service, in our case BizTalk Server.
  • Creates a new duplex channel.
  • Invokes the AskQuestion method.

Configuration File

The final step was to define the client endpoints in the client configuration file in order to invoke the 3 WCF Receive Locations exposed by the BizTalk application.

Configuration File

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <netNamedPipeBinding>
        <binding name="netNamedPipeBinding" />
      </netNamedPipeBinding>
      <netTcpBinding>
        <binding name="netTcpBinding">
          <security mode="Transport">
            <transport protectionLevel="None" />
          </security>
        </binding>
      </netTcpBinding>
      <wsDualHttpBinding>
        <binding name="wsDualHttpBinding" />
      </wsDualHttpBinding>
    </bindings>
    <client>
      <clear />
      <endpoint address="net.tcp://localhost:7171/magic8ballbiztalk/sync"
                binding="netTcpBinding" 
                bindingConfiguration="netTcpBinding"
                contract="IMagic8BallBizTalk"
                name="NetTcpEndpointBizTalk" />
      <endpoint address="net.pipe://localhost/magic8ballbiztalk/sync" 
                binding="netNamedPipeBinding"
                bindingConfiguration="netNamedPipeBinding" 
                contract="IMagic8BallBizTalk"
                name="NetNamedPipeEndpointBizTalk" />
      <endpoint address="http://localhost/magic8ballbiztalk/syncmagic8ball.svc"
                binding="wsDualHttpBinding" 
                bindingConfiguration="wsDualHttpBinding"
                contract="IMagic8BallBizTalk"
                name="WsDualHttpEndpointBizTalk" />
    </client>
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

We are now ready to test the solution.

Testing the Solution

To test the application, you can proceed as follows:

  • Configure and start the DuplexMEP BizTalk application.
  • Open a new instance of the Client Application, as indicated in the picture below.
  • Enter an existential question like "Why am I here?", "What’s the meaning of like?" or "Will the world end in 2012?" in the Question textbox.
  • Select one of the following endpoints:
    • NetTcpEndpointBizTalk to invoke the DuplexMEP.Sync.WCF-NetTcp.ReceiveLocation.
    • NetNamedPipeEndpointBizTalk to invoke the DuplexMEP.Sync.WCF-NetNamedPipe.ReceiveLocation.
    • WsDualHttpEndpointBiztalk to invoke the DuplexMEP.Sync.WCF-Custom.WsDualHttpBinding.ReceiveLocation.
  • Specify a Delay in seconds in the corresponding textbox.
  • Press the Ask button.

Now, if you press the Ask button multiple times in a row, you can easily notice that the client application is called back by BizTalk in an asynchronous way. Therefore, the client application doesn’t need to wait for the response to the previous question before posing a new one.

In order for the demo to work, it’s important that the Delay in seconds that you specify in the client application is less than the ReceiveTimeout configured in the WCF Receive Location. When using a WCF Adapter other than the WCF-Custom and WCF-CustomIsolated, you cannot specify a custom ReceiveTimeout. The following picture shows the Binding tab of a WCF-NetTcp Receive Location.

As a consequence, the default 10 minutes timeout will be used. Therefore, if you need to increase the ReceiveTimeout beyond 10 minutes, you can replace the WCF Adapter in question with the WCF-Custom and WCF-CustomIsolated adapter and select the same binding. This way, you can specify a custom value for the ReceiveTimeout property exposed by the binding, as shown in the following picture:

Conclusions

In this article we have seen how to exchange messages with an orchestration via a two-way WCF Receive Location using the Duplex Message Exchange Pattern. In the next article of the series, we’ll see how to implement an asynchronous communication between our client application and a WCF Workflow Service running within IIS\AppFabric Hosting Services using the Durable Duplex Correlation. In the meantime, here you can download the companion code for this article. As always, you feedbacks are more than welcome!

Acknowledge review and comments from Valery Mizonov. Thanks mate!

Calling Workflow Services without Add Service Reference

Sometimes you just don’t want to do an Add Service reference in the client application but still be able to to call a WF4 workflow service. The good thing is that a WF4 workflow service is just another WCF service from the client perspective so almost everything you can do with a regular WCF service you can also do with a workflow service. And calling the service without doing an Add Service Reference first is one of those things.

In these examples I am going to use the default workflow service template just to make it easy to get started. Adjust the client code depending on your service contract.

 

The first option is to hand craft the service contract on the client and use the ChannelFactory<T>

This option is very simple and straightforward. Basically you are handcrafting a ServiceContract and request/response MessageContract’s to fit the service.

The ServiceContract is nice and simple and looks like this:

[ServiceContract(Name = "IService")]
interface IMyService
{
    [OperationContract]
    GetDataResponse GetData(GetDataRequest request);
}

 

The request and response messages are defined using a MessageContract like this:

[MessageContract(IsWrapped = false)]
class GetDataRequest
{
    [MessageBodyMember(Name = "int", 
        Namespace = "http://schemas.microsoft.com/2003/10/Serialization/")]
    public int Value { get; set; }
}
 
[MessageContract(IsWrapped = false)]
class GetDataResponse
{
    [MessageBodyMember(Name = "string", 
        Namespace = "http://schemas.microsoft.com/2003/10/Serialization/")]
    public string Value { get; set; }
}

 

With these pieces in place calling the workflow service is simple using the following code:

static void Main(string[] args)
{
 
    var factory = new ChannelFactory<IMyService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:9199/Service1.xamlx"));
    var proxy = factory.CreateChannel();
    var response = proxy.GetData(new GetDataRequest() { Value = 42 });
    Console.WriteLine(response.Value);
    Console.ReadLine();
}

 

Note the only thing that varies here is the endpoint address.

 

Getting rid if the ServiceContract

If you don’t want to use a fixed ServiceContract you can use the low level WCF Message API. In this case we use a very simple service contract with the action name of “*” and request/response of type Message. This means we can send any request to any request/response service through this contract. The contract is very simple:

[ServiceContract]
interface IGenericService
{
    [OperationContract(Action = "*")]
    Message GetData(Message request);
}

 

 

The complexity now is in constructing the messages to send but even that isn’t to bad. There are may ways to do this, I chose the XElement API here but you are free to do it any way you like.

static void Main(string[] args)
{
    XNamespace ns = "http://schemas.microsoft.com/2003/10/Serialization/";
 
    var factory = new ChannelFactory<IGenericService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:9199/Service1.xamlx"));
    var proxy = factory.CreateChannel();
    var request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IService/GetData", new XElement(ns + "int", 42));
    
    var response = proxy.GetData(request);
 
    var xml = (XElement)XElement.ReadFrom(response.GetReaderAtBodyContents());
    var message = xml.Value;
    Console.WriteLine("Response is: {0}", message);
 
    Console.ReadLine();
}

 

With this in place you can create a generic client that can send messages to any WCF service, workflow service or plain old WCF, all you need to do is store the endpoint, operation name and message formats somewhere. I have to say I really like this power and have used it in several applications where flexibility and configurability was important.

 

Enjoy!

www.TheProblemSolver.nl

Wiki.WindowsWorkflowFoundation.eu

BizTalk 2010: BizTalk Feature Pack Released

Hi Folks I wanted to let you know about some new BizTalk details with the release
of a Feature pack.

The main component of the Feature Pack is to install a new component called ’BizTalk
Server 2010 AppFabric Connect for Services’
– which in essence provides cloud
based endpoints for your BizTalk deployments (based on .NET services)

Download from HERE

I’m rolling my up my sleeves and looking further into it.

(There’s an MSDN blog post on it – http://blogs.msdn.com/b/biztalk_server_team_blog/archive/2010/10/21/biztalk-server-appfabric-connect-for-services.aspx of
which 2 screen shots below are taken from it)

Which gives you Azure/.NET Service Bus WCF bindings seen from within BizTalk of the
following:

I haven’t been able to see Queuing support from within the Binding,
previously this was custom code that was written when establishing a connection to
the .Net Service Bus.
(this gives you the ability to publish msgs out to the ’bus’ and clients can be either
connected or disconnected and when they reconnect, the msgs will flow from their last
one.)

Interesting times,

Mick.

StreamInsight: Understanding dynamic query composition

Been tied up with PASS for the past (pun intended) couple of weeks, so it’s time to get a bit caught up on writing.  One of the key technical features of StreamInsight is the ability for one query to consume the output of another, enabling the system to avoid having to process events twice, and opening up a new world of flexibility.  The feature that unlocks these capabilities is Dynamic Query Composition.  That being said, some of the nuances of how StreamInsight constructs queries and flows events is not always obvious.  As this has resulted in me making a few sub-optimal design choices in the past , I figured I’d put together a little article demonstrating the subtle nuances involved when using (or not using) DQC.

For more background on DQC, please refer to:

Take the diagram below, illustrating a common query composition pattern.  Given a single input stream (represented by the inputAdapter shape in the diagram), we would like to consume the stream of events from that adapter in both queryOne and queryTwo.

At first glance, this query syntax would seem to fit the bill (note that I’m using the new IObservable support for the input adapter, but using the classic adapter syntax to create the queries bound to an output adapter, to explicitly show query creation):

Code Snippet
  1. // Convert the data source into a temporal stream
  2. var orderStream = ordersSimple.ToPointStream(cepApp, s =>
  3. PointEvent.CreateInsert(s.StartTime, s),
  4. AdvanceTimeSettings.IncreasingStartTime);
  5.  
  6. // Create a query in two parts
  7. var queryOne = from e in orderStream where e.OrderID > 50 select e;
  8. var queryTwo = from e in queryOne.TumblingWindow(
  9. TimeSpan.FromDays(1), HoppingWindowOutputPolicy.ClipToWindowEnd)
  10. select new
  11. {
  12. OrderCount = e.Count()
  13. };
  14.  
  15. // Convert these templates into queries, bound to an output adapter
  16. var tracerConfig = new TracerConfig()
  17. {
  18. DisplayCtiEvents = false,
  19. SingleLine = true,
  20. TracerKind = TracerKind.Console
  21. };
  22.  
  23. var queryOneRun = queryOne.ToQuery(cepApp, "QueryOne", "",
  24. typeof(TracerFactory), tracerConfig, EventShape.Point,
  25. StreamEventOrder.FullyOrdered);
  26.  
  27. var queryTwoRun = queryTwo.ToQuery(cepApp, "QueryTwo", "",
  28. typeof(TracerFactory), tracerConfig, EventShape.Interval,
  29. StreamEventOrder.FullyOrdered);

However, this wouldn’t produce the desired result.  Instead, the StreamInsight engine will construct the query pattern seen in the diagram below – where queryTwo composes the design of queryOne, but not the runtime stream (i.e. creates two adapter instances).

This can be verified by using the debugger to check the input source of the query.  For details on how to use the debugger, see my blog post here.

  1. Open the Event Flow Debugger, and connect to your StreamInsight instance (walkthrough on using the Event Flow Debugger here).
  2. Navigate to your queryTwo definition, and Show Query.
  3. As seen in the diagram below, if the input adapter is an actual input adapter (and not a published stream) you have two independent streams each with their own input adapter.

 

In order to have the two queries compose at runtime, we need to:

  1. Publish the results of the first query as a published stream with a strongly typed output (published schemas cannot have anonymous types)
  2. Consume the published stream as the input source of the second query.

Updating the query syntax to take advantage of DQC looks like:

Code Snippet
  1. // Create a simple data source (oData feed), using a non-anonymous
  2. // type
  3. var ordersSimple = from o in northwind.Orders
  4. where o.OrderDate != null && o.ShippedDate != null
  5. select new NorthwindOrderResult
  6. {
  7. StartTime = (DateTime)o.OrderDate,
  8. EndTime = (DateTime)o.ShippedDate,
  9. OrderID = o.OrderID,
  10. ShipRegion = o.ShipRegion,
  11. CompanyName = o.Customer.CompanyName
  12. };
  13.  
  14. // Convert the data source into a temporal stream
  15. var orderStream = ordersSimple.ToPointStream(cepApp, s =>
  16. PointEvent.CreateInsert(s.StartTime, s),
  17. AdvanceTimeSettings.IncreasingStartTime);
  18.  
  19. // Create a query
  20. var queryOne = from e in orderStream where e.OrderID > 50 select e;
  21. var queryOneRun = queryOne.ToQuery(cepApp, "QueryOne", "",
  22. typeof(TracerFactory), tracerConfig, EventShape.Point,
  23. StreamEventOrder.FullyOrdered);
  24.  
  25. // Convert the query's output into a published stream (with a
  26. // non-anonymous type)
  27. var queryOneStream = queryOneRun.ToStream<NorthwindOrderResult>();
  28.  
  29. // Bind the second query to the published stream
  30. var queryTwo = from e in queryOneStream.TumblingWindow(
  31. TimeSpan.FromDays(1), HoppingWindowOutputPolicy.ClipToWindowEnd)
  32. select new
  33. {
  34. OrderCount = e.Count()
  35. };
  36. // Run the second query
  37. var queryTwoRun = queryTwo.ToQuery(cepApp, "QueryTwo", "",
  38. typeof(TracerFactory), tracerConfig, EventShape.Interval,
  39. StreamEventOrder.FullyOrdered);

Now that we’ve updated the query structures to use DQC, let’s take another look at the query structure in the debugger.  Note that the input source for the second query is now a published stream (as it is now bound to a URI source, not a direct input adapter).

There we go – we can now feed the run-time results of one query into another query using Dynamic Query Composition.

Hands-On Lab: Building Windows Azure Applications with the Caching Service

Recently my team quietly released an update to the Windows Azure Platform Training Course (the training course is similar to the training kit, with minor differences) to coincide with new services released at PDC10.  There’s a lot of nice updates in this release, including a new hands-on lab called Building Windows Azure Applications with the […]

Starting ALL.Exceptions 0xC00CD557

There are two binding files included in the ESB toolkit 2.1 that are made available if you want to make changes to the bindings.

WARNING: DON”T MODIFY THE BINDING FILES WITH AN INTELLIGENT XML MODIFICATION TOOL (like Visual Studio)

You will make the modifications, import the binding file, and it will import it correctly, and when you go to start the Microsoft.Practices.ESB, you will get this error:

The issue is that it changes the filter from:

<Filter>&lt;?xml version="1.0" encoding="utf-16"?&gt;
	&lt;Filter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
	&lt;Group&gt;
	&lt;Statement Property="ErrorReport.FailureCode" Operator="6" /&gt;
	&lt;/Group&gt;
	&lt;Group&gt;
	&lt;Statement Property="Microsoft.Practices.ESB.ExceptionHandling.Schemas.Property.FaultCode" Operator="6" /&gt;
	&lt;/Group&gt;
	&lt;/Filter&gt;</Filter>

to

<Filter>
        &lt;?xml version="1.0" encoding="utf-16"?&gt;
	&lt;Filter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
	&lt;Group&gt;
	&lt;Statement Property="ErrorReport.FailureCode" Operator="6" /&gt;
	&lt;/Group&gt;
	&lt;Group&gt;
	&lt;Statement Property="Microsoft.Practices.ESB.ExceptionHandling.Schemas.Property.FaultCode" Operator="6" /&gt;
	&lt;/Group&gt;
	&lt;/Filter&gt;
</Filter>

Lesson to learn: the filter settings within the binding file need to be correctly formatted xml (spaces before the first open carrot is illegal)

Making Azure apps configurable again with the new Azure PDC features

Early Thinking

This post describes some new Azure features and how they might help us in leveraging the power of the web/app.config files again.  The content is this article is written, before any CTP/Beta versions of these features are available, so the details might be incorrect and there might be some wrong assumptions.  I will verify all of these parts, once the features are made available.

Current situation

Until now, an important difference between deploying a web application (or web service) on a local or hosted server and deploying a web application (or web service) to Windows Azure was the usage of the web.config file.

In a standard web application (hosted on IIS), the web.config can easily be changed to update configuration settings.  When changing this file, the web application gets refreshed and the new settings are being applied.

On Azure, there was a total different story.  The web.config was compiled with the deployment package and to change it, a new version of the packaged needed to be uploaded, resulting in a lot of work/time before the new settings got applied.  Luckily, Windows Azure provided a nice alternative: the serviceconfiguration files (.cscfg).  This file allowed to specify configuration values that could be loaded at runtime through the following code:

RoleEnvironment.GetConfigurationSettingValue("myValue"); code.

Painpoints with the Azure serviceconfiguration files.

The idea is great, having service configuration files managed on Role level and not on instance level.  But there are some big disadvantages in the following scenarios:

WCF Configuration settings

WCF has tons of configuration settings and I am a big fan of having my service endpoints configured through the .config files.  This allows for easy change and full declarative configuration, without having the settings (binding/behaviors) compiled with the program.  And since the introduction of the WCF Router service, even more configuration (the pub/sub logic) gets in the .config file.

Having all these settings in the Azure .cscfg file, would be very hard to maintain, or we’d need to switch to custom configuration files.

Custom configuration settings

In most of the applications that are developed at Codit, we use custom configuration sections to have a better structured configuration experience.  These configuration sections are complex types and no standard name/value configuration pairs.  Migrating these applications to Azure, would mean our configuration logic would need to be totally redesigned.

ASP.NET settings

ASP.NET also include a lot of settings that might need to be changed at runtime.

PDC announcements

At PDC a lot of stuff was announced that can help us here.  This article described the various announcements and their impact on the configuration issues I described above.

Remote desktop capabilities

Through the updated Azure management portal (didn’t that look a 100 times nicer than the current one?), we will soon be able to have Remote Desktop access to our web.  This will be done by adding the following Modules in the service definition file (.csdef)

<Imports>

<Import moduleName="RemoteAccess" />

<Import moduleName="RemoteForwarder" />

</Imports>

This allows us to connect to a running instance of an Azure role.  This way we can alter the web.config file and have everything updated as on a regular web application.

The biggest disadvantages of this approach are the following:

  • When having multiple instances (which is required to have High Availability), we need to remote in on every running instance of our web role.
  • Our changes will be undone when our instance restarts or gets redeployed, because the machine gets ’prepped’ again, resulting in a fresh installation of our Azure package.

The new Azure VM Role

Through the announced Windows Azure VM Role, we will be able to customize our own Virtual Machine, by uploading a pre-built virtual hard disk (VHD) to a blob storage account.

The steps to do this are the following :

    • Install Windows Server 2008 R2 Enterprise on the VHD
    • Add any Additional software you might need on the VM Role instances
    • Install Windows Azure Integration Components that include the following components
      • Agent
      • Runtime interface (topo, config, shutdown notification)
      • Remote Desktop configurator
      • Windows Azure Drives driver
    • Generalize the VHD is recommended, by using sysprep  (machine name will be assigned by Azure.)

By using a difference VHD, it might be easy to upload a new version of that VHD with the updated configuration file on it and have the changes applied.  Besides that, the same approach can be taken as described in the previous section (remote desktop capabilities).

The biggest disadvantages of this approach are the following:

  • To upload a new version of the VHD difference disk, the role might need to be reinitiated (to be tested, once the VM Role is available for CTP)
  • Cumbersome way of changing the web.config.

Startup tasks

As shown in the CloudCover show of two weeks ago (watch episode), there is a possibility of executing startup tasks, when the role instance starts up.  This looks like the best suited feature to solve our config file problem.

I am making one crucial assumption here (that I’ll check once this is made available):  I believe the start up tasks are being executed prior to the Role Instance initiation – so before the web application is started.  This means we can still change the web.config without having the web application being recycled automatically.

Leveraging the startup tasks will allow us to take the web.config from a configurable blob container and write it to the application directory on the Azure instances.  Therefore, we just need to write a script that performs this logic.  (code will be in a future post).  Then we just need to configure that script in the Startup element in the csdef configuration file:

<Startup>
     <Task commandline="updateconfig.cmd" executionContext="elevated" 
               taskType="simple/foreground/background"
/>
</Startup>

This approach looks like the best approach to make our web.config and app.config files configurable again.

Conclusion

A lot of uncertainties, because there is no bit available yet to test this with.  So, please expect a reviewed post, once it has been made available. 
But one thing is for sure: the new Azure features will make Azure application development much more flexible again.

Sam Vanhoutte, Codit

London Connected Systems User Group Session

EMC Consulting will be hosting a ’Connected Systems’ user group session at their London office on Tuesday 7th December. 

What is ’Connected Systems’ you ask? Well, this encompasses any technology that is related to connecting systems together, which happens to be quite a broad subject. The user group focuses on Microsoft technology in particular such as WCF, WF, Azure and SSIS but is keen to also cover patterns and practices, those that are technology independent. Essentially, if you have experience or an interest in working on solutions that require any interaction with an external system then this user group could be appealing.

The first event contains two sessions. The first will be Liberating Identity using Windows Identity Foundation by Simon Evans. The second will be on some work by Prasanna Krishnan relating to Large Data Streaming with WCF

This is the inaugural session and we are very interested in hosting many more events. The venue will be available for free use, so the only concern is making sure we have people that can provide some interesting content they wish to share with the rest of the community. Please feel free to come along and offer suggestions for material going forward. Alternatively, contact me directly.

For details on the first event and register your place now, please visit http://lcsug.eventbrite.com/