Share via


Implementing Adapter Inbound Message Exchange Handler - Part 2 (Consumption in .NET Console Application using WCF Service Model)

In my previous post, I covered the Adapter Developer perspective on creating a sample WCF-based adapter with Inbound Message Exchange Handler support. Once the inbound handler implementation is complete in the adapter, the adapter is ready for consumption. In this post, I am going to give high level overview of how it can be consumed in different applications.

Console Application using WCF Service Model and Add Adapter Service Reference Visual Studio Plug-In

The target system (LOB) is the initiator in this case and the Adapter Consumer will provide the Service Implementation as an acceptor. Remember, in our Hello World scenario, there isn’t a real LOB making a call into the service that the Adapter Consumer is going to implement. Since we are just simulating a LOB, in our Inbound Handler, a file folder is being watched for a new *.txt file. When it encounters a new file in this folder, the inbound handlers sends a WCF message (Adapter Developer defines the contract – see previous post) to the WCF Service provided by the Adapter Consumer. Once the Service Implementation gets the message from the target application, the service can act on it and do whatever it needs in order to react to that. In this particular case, the service will not return any message back to LOB. Many times you may want to return some confirmation or other details once the message is processed by the service.

 

1) Create a new Visual Studio C# Console Application Project

2) Invoke Add Adapter Service Reference Visual Studio Plug-In

 

 

3) Choose the “Select contract type: “ to Service (Inbound Operations). The operation that we added as inbound in our Adapter Browse Handler implementation should show up here. Note that you cannot add both inbound and outbound operations in the same generated proxy. In my case since I have only one method added to the root node, it will show up once I select the root (/) node.

4) Select the Inbound Operation and click on OK

5) Two files should get generated

a. Service Interface (HelloWorldBindingClient.cs)

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]

[System.ServiceModel.ServiceContractAttribute(Namespace="hello://Microsoft.WCF.Samples.Adapters", ConfigurationName="HelloWorld")]

public interface HelloWorld {

   

    [System.ServiceModel.OperationContractAttribute(Action="Hello/OnReceiveGreeting", ReplyAction="Hello/OnReceiveGreeting/response")]

    void OnReceiveGreeting(string name, string data);

}

 

b. Application Configuration (app.config)

<configuration xmlns="https://schemas.microsoft.com/.NetConfiguration/v2.0">

    <system.serviceModel>

        <services>

            <service name="{Enter absolute name of Service Implementation class here.}">

                <endpoint

                    address="hello://dummy?action=Hello/OnReceiveGreeting"

                    binding="helloWorldBinding"

                    bindingConfiguration="HelloWorldAdapterBinding"

                    contract="HelloWorld" />

            </service>

        </services>

        <bindings>

            <helloWorldBinding>

                <binding name="HelloWorldAdapterBinding" closeTimeout="00:01:00"

                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"

                    count="0" overrideWsdlBuilder="false" />

            </helloWorldBinding>

        </bindings>

    </system.serviceModel>

</configuration>

 

The Inbound Handler reads a bunch of actions in the StartListener() method. How does it know about these actions? They can be provided by the Adapter Consumer through two ways – in the URI (as shown above) or by defining a custom attribute for the interface. The URI solution works but may not be elegant in situations where there is a need to listen on multiple actions.

6) Create a new C# file and implement the Service Interface in this file

Every time a new .txt file is dropped in the file watcher folder that the adapter is listening on, my service implementation will get called. In my service implementation, I just want to print this event to the console.

using System;

using System.Collections.Generic;

using System.Text;

using System.ServiceModel;

namespace TestHelloWorldAdapter_Service_Inbound

{

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

    class HelloWorldService : HelloWorld

    {

        #region HelloWorld Members

        void HelloWorld.OnReceiveGreeting(string name, string data)

        {

            Console.WriteLine("Received greeting from " + name + " with " + data);

        }

        #endregion

    }

}

 

7) Update app.config to link the Service Implementation i.e. put the service name (including the namespace) in service name=”{service name}”

            <service name="TestHelloWorldAdapter_Service_Inbound.HelloWorldService">

                <endpoint

                    address="hello://dummy?action=Hello/OnReceiveGreeting"

                    binding="helloWorldBinding"

                    bindingConfiguration="HelloWorldAdapterBinding"

                    contract="HelloWorld" />

            </service>

8) Host the service

We will host our service in a console application.

using System;

using System.Collections.Generic;

using System.Text;

using System.ServiceModel;

namespace TestHelloWorldAdapter_Service_Inbound

{

    class Program

    {

        static void Main(string[] args)

        {

            // Warning: not catching any exceptions in this snippet

            // Create a ServiceHost for service implementation and

            // use the base address from app.config

            ServiceHost host = new ServiceHost(typeof(HelloWorldService));

            InstanceContext instanceContext = new InstanceContext(new HelloWorldService());

            // Open the ServiceHost and start accepting connections

            host.Open();

            Console.WriteLine("The service is ready.");

            Console.WriteLine("Press <ENTER> to terminate service.");

            Console.ReadLine();

            // Close the ServiceHost

            host.Close();

        }

    }

}

 

9) Build and Run

Once the build is successful, you should be able to see a Console Window popup.

10) Test

The service is ready.

Press <ENTER> to terminate service.

Received greeting from c:\temp\helloworld\lang.txt with Salut, Hallo, Hey, Moin

Moin, Hola, Bonjour, Bon Soir, Bonne Nuit, Ciao, Hej, Hoi, Namaste!

Received greeting from c:\temp\helloworld\hello.txt with Hello Hello ...

 

Copy a *.txt file into the file watcher folder – make this folder an adapter binding property, so it can be set via app.config.

The service is listening for the messages coming from the adapter and the messages are created when the event is triggered by the File System Watcher. I copied two text files into the watcher folder and the above output was created, based on the service implementation.

Gotchas!!!!

One Way vs. Two Way Contracts

All WCF clients and services that communicate with the BizTalk WCF adapter are expected to be two way, with the exception of queues (such as NetMsmq). BizTalk expects a void response back from the service (or a non-void response if using a two-way send port). This is so that BizTalk knows when to delete the message from the Message Box and thereby ensuring no message loss. This means even if the Service is not expected to return any result, just make sure the generated interface doesn’t have an attribute “OneWay” set to true. This happens in Adapter SDK object model, when the OperationResult in OperationMetadata is set to NULL instead of OperationResult.EMPTY.

 

WCF message serialization / de-serialization

Since the Inbound Handler is dealing with XML classes to create an XML message that will be de-serialized by the dispatcher for the Service, ensure the message created is valid. The message being created should comply with how you have defined within the Adapter Resolver Handler. It can be common to get the System.ServiceModel.Dispatcher.NetDispatcherFaultException if either the incoming XML messages is invalid because of message not being compliant with the contract or if the data itself contains some bad characters not understood by the XML serializer. For example, the interface for the Hello World is quite straightforward as it expects only two elements in the request message that are of type string, however, if the string itself contains some invalid characters the seralization can fail. These exceptions may or may not show up once the service is started – depending on the adapter implementation. This leads to my next point – Tracing.

Tracing

Enable the WCF, Adapter SDK and Adapter tracing so you can debug and troubleshoot the service, when you are not getting the exepcted outcome. 

 

Next time: Testing adapter inbound handler implementation in BizTalk Server 2006 R2 using BizTalk WCF Adapter and Consume Adapter Service BizTalk Project Add-In