Sending XML messages to BizTalk using the WCF Channel Model

Posted: August 9, 2009  |  Categories: BizTalk Uncategorized
Tags:

It is quite a common requirement to have a generic receive location in BizTalk to accept any XML file. Richard Seroter has listed a few options already on how to do this.

I looked into how BizTalk exposes its WCF receive locations and how they could be consumed by using the WCF channel model, without having to use WSDLs or proxy classes.

Creating the BizTalk Receive Location

In my scenario I created a one way BizTalk receive port, and a receive location with the WCF.NetTcp adapter and the XMLReceive pipeline (but you could choose other WCF adapters here):

image

I then configured the WCF-NetTcp adapter properties by entering a URI and changing the security mode to None (for simplicity sake):

image image

 

The WCF Adapter Interfaces

The main part of the BizTalk WCF receive locations is in Microsoft.BizTalk.Adapter.Wcf.Runtime.dll found on the installation folder  of BizTalk 2006 R2 and 2009.

As already noted by Paolo Salvatori “the WCF Receive Adapter instantiates a singleton instance of the BizTalkServiceInstance class for each WCF Receive Location”. The BizTalkServiceInstance class declaration implements the following service contract interfaces:

·         ITwoWayAsync
·         ITwoWayAsyncVoid
·         IOneWayAsync
·         IOneWayAsyncTxn
·         ITwoWayAsyncVoidTxn

 

Each of these cover the different scenarios of the WCF receive locations. They all have a BizTalkSubmit method, but if you inspect the BizTalkServiceInstance class’ implementation of this method (for all of the interfaces) you’ll notice that it’ll throw a NotSupportedException exception, so you can’t use this method to submit messages to the BizTalkServiceInstance instance. Read up Paolo’s post for some more information. Here’s the BizTalkSubmit implementation for the ITwoWayAsynchVoid for example:

[OperationBehavior]
void ITwoWayAsyncVoid.BizTalkSubmit(Message message)
{
   throw new NotSupportedException();
}

What is made available to us though is the server side IAsyncResult model. This model is well explained in this blog post by Dan Rigsby . The methods to use to send messages to BizTalk WCF receive locations are the other two method of BizTalkServiceInstance that implement the IAsyncResult mode class: BeginTwoWayMethod (or BeginOneWayMethod for the IOneWay… interfaces) and EndTwoWayMethod (or EndOneWayMethod).

 

Sending Messages to the Receive Location

Now that we have a BizTalk receive location and a basic understanding of the WCF receive adapters we can move on to sending a message to it from our .NET code. First add a reference to System.ServiceModel.dll and System.Runtime.Serialization.dll. Our one way receive location uses the ITwoWayAsyncVoid interface (the IOneWay… interfaces are only for the WCF-NetMsmq adapter, and the ones ending in Tx are for when transactions are enabled on the receive location. Interfaces without Void are for receive response):

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

You can get the other service contracts from Paolo’s post or from Microsoft.BizTalk.Adapter.Wcf.Runtime.dll.

Once you have the appropriate service contract interface in your code you can now configure the end point. I added mine to my app’s App.Config file with the address configured on the BizTalk WCF receive location and a binding that follows the one configured there:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="NetTcpBinding_ITwoWayAsyncVoid">
          <security mode ="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint
        address="net.tcp://bt09standalone.thiago.com/WCFNetTcpGenericReceive"
        binding="netTcpBinding"
        bindingConfiguration="NetTcpBinding_ITwoWayAsyncVoid"
        contract="Microsoft.BizTalk.Adapter.Wcf.Runtime.ITwoWayAsyncVoid"
        name="BlogWCFGenericReceiver" />
    </client>
  </system.serviceModel>
</configuration>

Here is the BeginTwoWayMethod method declaration for the ITwoWayAsyncVoid interface:

[OperationBehavior]
IAsyncResult ITwoWayAsyncVoid.BeginTwoWayMethod(Message message, AsyncCallback callback, object state); 

You pass it a WCF message, and can also pass an async callback method so that the main thread isn’t blocked. You can also pass in an object in the state parameter and you will get the same object back once the callback method is called.

Alternatively from passing in a callback method you can just tell the wait handle to block the current thread until a signal is received – once it is received this is when BizTalk is ready for you to call the EndTwoWayMethod.

Here is the method in my windows forms app that sends a message to BizTalk and uses the wait handle to wait for a signal:

private void btnSubmitWaitHandle_Click(object sender, EventArgs e)
{         

    txtResults.Text += DateTime.Now.TimeOfDay.ToString() 
                         + " Sending message to BizTalk\r\n";
    DisableForm();

    try
    {
        cf = new ChannelFactory<ITwoWayAsyncVoid>("BlogWCFGenericReceiver");
        cf.Open();
        ITwoWayAsyncVoid waitHandleProxy = cf.CreateChannel();

        btnSubmitWaitHandle.Enabled = false;

        Channels.Message msg =  
            Channels.Message.CreateMessage(Channels.MessageVersion.Default, 
            "BeginTwoWayMethod", new XmlTextReader(new StringReader(txtMessage.Text)));

        //Start the call to BizTalk and get the async result back
        IAsyncResult res = waitHandleProxy.BeginTwoWayMethod(msg, null, null);

        //Blocks current thread until until AsyncWaitHandle receives a signal
        res.AsyncWaitHandle.WaitOne();

        //End the call once the wait handle signal is received from BizTalk
        waitHandleProxy.EndTwoWayMethod(res);

        txtResults.Text += DateTime.Now.TimeOfDay.ToString() + 
            " Message sent successfully\r\n";
        cf.Close();
    }
    catch (Exception ex)
    {
        txtResults.Text += DateTime.Now.TimeOfDay.ToString()
          + " Exception: " + ex.Message + "\r\n";
        if (cf != null) { cf.Abort(); }

    }
    finally
    {
        EnableForm();
        if (cf.State == CommunicationState.Opened)
        { cf.Close(); }
    }
}

And here are the two methods that send a message to BizTalk using the callback approach:

private void btnSubmitAsyncCallback_Click(object sender, EventArgs e)
{
    cf = new ChannelFactory<ITwoWayAsyncVoid>("BlogWCFGenericReceiver");
    cf.Open();
    asyncCallbackProxy = cf.CreateChannel();
    string state = Guid.NewGuid().ToString();
    txtResults.Text += DateTime.Now.TimeOfDay.ToString()
                         + " Message " + state + " sent to BizTalk\r\n";

    //Create message
    Channels.Message msg = 
        Channels.Message.CreateMessage(Channels.MessageVersion.Default,
        "BeginTwoWayMethod", new XmlTextReader(new StringReader(txtMessage.Text)));

    //Start the call to BizTalk and pass in the async callback method
    DisableForm();
    IAsyncResult res = asyncCallbackProxy.BeginTwoWayMethod(
       msg, new System.AsyncCallback(OnEndTwoWayMethod), state);
}

public void OnEndTwoWayMethod(IAsyncResult asyncResult)
{
    //Get the guid back, this will be what we passed as 'state' on BeginTwoWayMethod
    string msgid = asyncResult.AsyncState.ToString();
    try
    {
        //End the call to BizTalk. Make sure it's the same asyncCallbackProxy
        //object instance used to send the message otherwise it will fail
        asyncCallbackProxy.EndTwoWayMethod(asyncResult);

        this.Invoke(new MethodInvoker(delegate()
            { txtResults.Text += DateTime.Now.TimeOfDay.ToString() +
                " Message " + msgid + " sent successfully\r\n"; }));
    }
    catch (Exception ex)
    {
        this.Invoke(new MethodInvoker(delegate()
            { txtResults.Text += DateTime.Now.TimeOfDay.ToString() 
                + "Message: " 
                + msgid + ". Exception: " + ex.Message + "\r\n"; }));
        if (cf != null) { cf.Abort(); }
    }
    finally
    {
        this.Invoke(new MethodInvoker(delegate()
        { EnableForm(); }));
        if (cf.State == CommunicationState.Opened)
        { cf.Close(); }
    }
}

That’s it! We can now send any XML message to BizTalk this way. Here’s what my tester application looks like after two messages were sent with the WaitHandle and once with the callback approach:

image

If the BizTalk receive location is disabled or the BizTalk host is stopped, you get a System.ServiceModel.EndpointNotFoundException exception when calling the EndTwoWayMethod method (I removed my machine’s ip address from the exception):

“Could not connect to net.tcp://bt09standalone.thiago.com/WCFNetTcpGenericReceive. The connection attempt lasted for a time span of 00:00:02.0141069. TCP error code 10061: No connection could be made because the target machine actively refused it ip address:808.”

What if, for example, we change the Inbound BizTalk message body from Body to Path and enter an invalid path?

If you have ‘Include exception detail in faults’ enabled you get an exception of type  System.ServiceModel.FaultException<System.ServiceModel.ExceptionDetail> with the right details:

“Unable to find match for inbound body path expression "/nothing" in message.”

If the tickbox is not enabled you get a System.ServiceModel.FaultException:

“The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.”

If you untick the ‘Suspend request message on failure’ and there is an exception in the pipeline, the message doesn’t get suspended and the client gets the error back when ‘Include exception detail in faults’ is ticked:

“There was a failure executing the receive pipeline: "Microsoft.BizTalk.DefaultPipelines.XMLReceive, Microsoft.BizTalk.DefaultPipelines, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Source: "XML disassembler" Receive Port: "Blog.GenericReceive" URI: "net.tcp://localhost/WCFNetTcpGenericReceive" Reason: Finding the document specification by message type "http://Blog.GenericReceiver.BT/Schemas#aGenericReceiverSample" failed. Verify the schema deployed properly. ”

So there you have it, another way of submitting any XML messages to BizTalk by using the WCF channel model.

 

Download the sample code

 

Regards,

Thiago Almeida

6 thoughts on “Sending XML messages to BizTalk using the WCF Channel Model”

  1. Nice writeup Thiago.

    How would you go about handling flat files or binary files in this context? Wrap them in an xml element?

    1. Hi Jan,

      In that case you are probably better off creating your implementation of the BodyWriter class that wraps the string you want to send around an XML element and then converts the string to a byte array, as described here:
      http://www.codeproject.com/Articles/34632/How-to-pass-arbitrary-data-in-a-Message-object-usi.aspx?display=Print

      Then on your WCF receive location change the Inbound BizTalk Message Body to Path, se the path to /Binary and change the Node Encoding to Base64.

      I have updated the source code and binding files in the download to add this option.

      Regards,
      Thiago

  2. Thanks for responding, Thiago.

    This is the same approach I’m using, even though I was hoping to find a more generic way of handling data in this scenario. I really wouldn’t like to care if I’m receiving (or sending) xml, flat files, binary or what have you.

    Anyway, thanks for taking the time to respond.

    Regards,
    Jan

  3. Thanks for the great post.

    Helped very much, was the exact the scenario I am currently looking at implementing.

Comments are closed.

BizTalk360
BizTalk Server

Over 500+ customers across
30+ countries depend on BizTalk360

Learn More
Serverless360
Azure

Manage and monitor serverless
components effortlessly

Learn More
Atomicscope
Business Users

Monitor your Business Activity in iPaaS
or Hybrid integration solutions

Learn More

Back to Top