Building Duplex Services with WCF

By | May 11, 2015

Introduction

To begin, consider non-duplex Request-Reply services. In non-duplex Request-Reply services, the client sends a request to the server and the server in turn replies with a response to the client. Another noteworthy characteristic of non-duplex Request-Reply services is that the client is always the one to initiate communication between the two parties. In duplex services, however, the server is able to initiate communication with the client as well. Often times (but not required) the operations in a duplex service are One-Way (and not Request-Reply) meaning that the request is simply sent and no response is returned or expected.

WCF (Windows Communication Foundation) is a part of the .NET Framework and provides a unified programming model for efficiently building service-oriented applications. With WCF you can build SOAP based web services as well as RESTful web services. Although it should be stated that for RESTful services the ASP.NET Web API is the more appropriate choice.

WCF deployments are broken up into the following four parts:

  1. Service Contract – An interface for stating which operations a given service supports. This component is known to both client and server as it is an agreement of what operations a service will provide to a client.
  2. Service Library – This is the implementation of the service contract. This is the code that the service executes when the client makes a request and as such it is known only to the service.
  3. Service Host – This is the process which hosts the service so that clients can call into the service. This process can be a console application, IIS, etc. and runs on the server machine.
  4. Client Application – This is the client application which the end user will utilize and it consumes the service. This can be a Console application, Windows Form application, WPF application, ASP.NET Web application, etc. and runs on the client machine.

My personal preference when owning both ends of the wire is to break up these parts into their own separate Visual Studio projects within a solution.

Example Application – Grocery List Duplex Service

So to demonstrate the power and ability of duplex services using WCF, I have constructed a client and server system where users can all contribute to a single grocery list. In this system, everyone automatically gets an updated list whenever a new item is added to the list. In terms of architecture, this is an implementation of the Publish-Subscribe messaging pattern where the service acts a message broker for the system. Indeed duplex services allow for event-like behavior.

Here is what the system looks like whenever a client sends a new item to the service to add to the list and in turn each client receives an updated list from the service:

Client-Service relationship

Service Contract

Defining Service Contracts for duplex WCF services is different from non-duplex WCF services in the fact that a separate Callback Contract must be specified along with the Service Contract. The Callback Contract specifies the operations that a service can invoke on a client. Readers may also notice that the operations of a duplex service are typically marked as One-Way. One-Way operations help prevent deadlock scenarios in duplex services. Deadlocks occur when two (or more) units are waiting on the other to finish and therefore neither ever does. I will talk more about One-Way operations shortly.

Here is the implementation of the Service Contract:

[ServiceContract (CallbackContract=typeof(IGroceryListDuplexCallback))]
public interface IGroceryListDuplexService
{
    [OperationContract(IsOneWay = true)]
    void Connect();

    [OperationContract(IsOneWay = true)]
    void AddItem(string item);

    [OperationContract(IsOneWay = true)]
    void Disconnect();
}

Here is the implementation of the Callback Contract:

public interface IGroceryListDuplexCallback
{
    [OperationContract(IsOneWay = true)]
    void SendUpdatedList(List items);
}

As you can see the Service Contract contains Connect and Disconnect operations which will serve to enroll and withdraw clients from receiving updated lists. The AddItem operation of the Service Contract allows a client to add an item to the grocery list which is shared by all clients. Lastly, there is a single operation in the Callback Contract which is called SendUpdatedList – This operation sends the most most up-to-date list to the clients who are connected (subscribed) to the service.

Something you may notice is that all operations return void which is required by One-Way operations (signified in WCF by IsOneWay = true). You can think of One-Way operations as fire-and-forget operations where the sender simply makes the request and does not wait for a response (neither will a response message ever be generated). A common misinterpretation of One-Way operations is that they do not block on the caller (that is they are falsely believed to be asynchronous). One-Way operations will block until the outbound data has been written to the network connection. Events that can cause delays in outbound data being written are an overwhelmed receiver, missing endpoint or any complication in the receiver being able to read the information off the wire.

Service Library

So now that we know what operations the service supports, let’s look at how the service does it. The service object is a Singleton meaning that all service calls are handled by the same service instance and this behavior is controlled by InstanceContextMode being set to Single. This is done so that all subscribing clients can reference the same grocery list as well as there being a single global list of client callback channels. In order to make the service object a bit more efficient, the service instance is multithreaded so that each call be handled in parallel by the service and this is controlled by ConcurrencyMode being set to Multiple. There is a bit of complication as WCF requires us to handle the synchronization of the service object ourselves, but this can be handled easily enough by the SyncRoot pattern.

[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.Single, 
    ConcurrencyMode = ConcurrencyMode.Multiple)]
public class GroceryListDuplexService : IGroceryListDuplexService
{
    private static List<IGroceryListDuplexCallback> _callbackChannels = new List<IGroceryListDuplexCallback>();
    private static List<string> _groceryList = new List<string>();
    private static readonly object _sycnRoot = new object();

    ...
}

The Connect operation grabs the Callback Channel of the client and checks if it is not already stored in the Callback Channel list – if not then it is added to the list. Additionally, if the client is a newcomer it is sent the most up-to-date list of grocery items. The Disconnect operation just grabs the Callback Channel of the client and attempts to remove the channel from the list. As you can see since the service instance is multithreaded we take special care to prevent simultaneous access by multiple threads to our Callback Channel list when adding and removing channels.

public void Connect()
{
    try
    {
        IGroceryListDuplexCallback callbackChannel =
            OperationContext.Current.GetCallbackChannel();

        lock (_sycnRoot)
        {
            if (!_callbackChannels.Contains(callbackChannel))
            {
                _callbackChannels.Add(callbackChannel);
                Console.WriteLine("Added Callback Channel: {0}", callbackChannel.GetHashCode());
                callbackChannel.SendUpdatedList(_groceryList);
            }                   
        }
    }
    catch
    {
    }
}

public void Disconnect()
{
    IGroceryListDuplexCallback callbackChannel =
            OperationContext.Current.GetCallbackChannel();

    try
    {
           lock (_sycnRoot)
           {
               if (_callbackChannels.Remove(callbackChannel))
               {
                    Console.WriteLine("Removed Callback Channel: {0}", callbackChannel.GetHashCode());
               }
           }
    }
    catch
    {
    }
}

When adding items to the grocery list again we need to make sure that only one thread can access the list at a time. First, the grocery list running on the service is updated with the item provided by the client. Next, we need to broadcast the updated list to all connected clients. To give out an updated grocery list to each connected client we need to loop through the Callback Channel list and invoke the SendUpdatedList operation on each client. The iteration process is done backwards because we may need to remove channels from the list while looping through it. If the state of the channel is no longer opened or if the channel throws any sort of exception it is removed from the Callback Channel list.

public void AddItem(string item)
{
    lock (_sycnRoot)
    {
        _groceryList.Add(item);

        Console.WriteLine("-- Grocery List --");
        _groceryList.ForEach(listItem => Console.WriteLine(listItem));
        Console.WriteLine("------------------");

        for (int i = _callbackChannels.Count - 1; i >= 0; i--)
        {
            if (((ICommunicationObject)_callbackChannels[i]).State != CommunicationState.Opened)
            {
                Console.WriteLine("Detected Non-Open Callback Channel: {0}", _callbackChannels[i].GetHashCode());
                _callbackChannels.RemoveAt(i);
                continue;
            }

            try
            {
                _callbackChannels[i].SendUpdatedList(_groceryList);
                Console.WriteLine("Pushed Updated List on Callback Channel: {0}", _callbackChannels[i].GetHashCode());
            }
            catch (Exception ex)
            {
                Console.WriteLine("Service threw exception while communicating on Callback Channel: {0}", _callbackChannels[i].GetHashCode());
                Console.WriteLine("Exception Type: {0} Description: {1}", ex.GetType(), ex.Message);
                _callbackChannels.RemoveAt(i);
            }
        }
    }
}

Service Host

For the service host, I am just going to self-host the service in a console application. There is nothing particular or special about self-hosting duplex services over non-duplex services so I am not going to say much here. I will, however, talk about the bindings of WCF which support duplex communication – these bindings are WSDualHttpBinding, NetTcpBinding and NetNamedPipeBinding. HTTP is connectionless and therefore is a unidirectional protocol, therefore WSDualHttpBinding utilizes two HTTP channels in order to support duplex communication. For WSDualHttpBinding there is a channel from client-to-service and a channel from service-to-client. NetTcpBinding and NetNamedPipeBinding only use one channel since the protocols are bidirectional. I choose WSDualHttpBinding here as it is more interop-friendly than the other options.

Client Application

A duplex client is structured like so where each red arrow represents a channel:

Duplex Client Design

Proxy class

First off, we need to create a proxy in order to consume the service. By service, I specifically mean the operations that the client can invoke. The proxy could be be auto-generated using svcutil, but since I have a reference to the service contract on the client – I will just hand-code the proxy instead. To create a duplex proxy – We need to inherit the from the DuplexClientBase<T> class and implement the service contract as well as provide the necessary constructor(s). As you can see implementing the service contract interface in the proxy class is as trivial as forwarding the service contract calls to the Channel property provided by the DuplexClientBase<T> base class which retrieves the inner channel used to send messages to the service endpoint.

internal class GroceryListDuplexServiceClient 
        : DuplexClientBase, IGroceryListDuplexService
{
    public GroceryListDuplexServiceClient(InstanceContext callbackInstance, 
              WSDualHttpBinding binding, EndpointAddress endpointAddress) 
        : base(callbackInstance, binding, endpointAddress) 
    { 
    }

    public void Connect()
    {
        Channel.Connect();
    }

    public void AddItem(string item)
    {
        Channel.AddItem(item);
    }

    public void Disconnect()
    {
        Channel.Disconnect();
    }
}

Here is how one would declare an instance of the proxy class and use it:


...
var groceryListDuplexCallback = new GroceryListDuplexCallback();
groceryListDuplexCallback.ServiceCallbackEvent += HandleServiceCallbackEvent;

var instanceContext = new InstanceContext(groceryListDuplexCallback); 
var dualHttpBinding = new WSDualHttpBinding(WSDualHttpSecurityMode.None);
var endpointAddress = new EndpointAddress(ServiceEndpointUri);
_proxy = new GroceryListDuplexServiceClient(instanceContext, dualHttpBinding, endpointAddress);
_proxy.Open();
_proxy.Connect();
...

Callback Handler class

Now the most interesting part comes in implementing the callback contract on the client. By default, the callback behavior is to synchronize all calls on the current synchronization context. For a Windows Forms application such as our client this behavior would result in the code being run on the user interface thread. By setting UseSynchronizationContext to false the callback operations will instead be entrusted to worker threads. It is important that the operations occur on worker threads for two main reasons: (a) If not, then while the callback operations are executing, all other operations on the UI thread will be blocked which may be annoying to users as the callback operation may be invoked unexpectedly and could cause the application to noticeably hang (especially if the callback operation happens to be potentially long running). (b) If the application happens to block the UI thread at certain points (either due to unexpected behavior in the code or because the application is less than perfect) and a callback occurs then the channel/ proxy could become faulted as the client was unable to handle the request from the service.

By putting the callback operations onto a worker thread we have solved some problems, but now we need to be able to communicate the results obtained on the worker thread to the UI thread. At this point our pal AsyncOperationManager comes to the rescue – here is what MSDN has to say about this class:

Provides concurrency management for classes that support asynchronous method calls.

Sounds like the right class for the job. Now we are particularly interested in the SynchronizationContext property of the class which returns the correct synchronization context for any application model supported by the .NET Framework. For example, since our client application is a WinForm application it will return the WindowsFormsSynchronizationContext.

Now at this point you might be thinking – What is a synchronization context as it pertains to the .NET Framework? A synchronization context provides a way to queue a unit of work to a particular context. In our case it allows our worker thread to dispatch messages to the UI synchronization context. This is required as only the UI synchronization context is allowed to manipulate the UI controls – If we were to try to update the UI from another context it would result in an illegal operation (causing an exception to be thrown). You might be thinking that threads are in a one-to-one relationship with a synchronization context, but that would be generally wrong as threads can share the same synchronization context. There are cases such as WindowsFormsSynchronizationContext where there is such a one-to-one relationship as the context is a single UI thread. For a wonderful article on the topic which gives it the proper treatment – see Stephen Cleary’s MSDN Magazine article.

Now that we have a grasp on synchronization contexts in .NET, we use the Post method of the SynchronizationContext class to asynchronously queue messages to the UI synchronization context. The Post method takes two arguments: a special delegate called SendOrPostCallback which represents the callback method we want to execute when the message is dispatched to the UI synchronization context as well as an Object which is passed to the delegate. For our purposes, we create the SendOrPostCallback delegate by passing in the OnServiceCallbackEvent method that has been implemented in the Callback class. We also create an instance of the UpdatedListEventArgs class which has been defined in the project by passing the new list of items into the constructor. The delegate along with the event arguments class instance are used as arguments to the Post method and by doing this our event invocation method can obtain the event arguments when it is marshaled from the worker thread to the UI thread. Subscribers (such as the MainForm object) to our ServiceCallbackEvent can then handle the event when it is raised.

[CallbackBehavior(UseSynchronizationContext = false)]
internal class GroceryListDuplexCallback : IGroceryListDuplexCallback
{
    private SynchronizationContext _syncContext = AsyncOperationManager.SynchronizationContext;
    public event EventHandler<UpdatedListEventArgs> ServiceCallbackEvent;

    public void SendUpdatedList(List<string> items)
    {
        _syncContext.Post(new SendOrPostCallback(OnServiceCallbackEvent), 
             new UpdatedListEventArgs(items));
    }

    private void OnServiceCallbackEvent(object state)
    {
        EventHandler<UpdatedListEventArgs> handler = ServiceCallbackEvent;
        UpdatedListEventArgs e = state as UpdatedListEventArgs;

        if (handler != null)
        {
            handler(this, e);
        }
    }
}

Main Form

The MainForm object subscribes to the ServiceCallbackEvent of the Callback class with the following handler:

private void HandleServiceCallbackEvent(object sender, UpdatedListEventArgs e)
{
     List<string> groceryList = e.ItemList;
     if (groceryList != null && groceryList.Count > 0)
     {
          groceryListBox.DataSource = groceryList;
     }
}

The rest of the client application code is not specific to Duplex WCF clients and so it is omitted from discussion in this blog post. However, the full source code of the solution can be downloaded at the end of this article.

Conclusion

So this concludes this introductory look at building duplex services using WCF. At some points, it may seem that there are extraneous or ultimately unnecessary details for the minimal scope of the tutorial project. However, I would like to borrow a bit from Einstein and say that I believe that tutorials should always be simple, but never too simple. We may have been able to get away with less robust methods in this tutorial, but I think a tutorial show not just work and teach the basics, but also showcase the best practices and generally the most powerful approaches. If at all possible, I would like to prevent giving readers just enough rope to hang themselves with and instead give them a tutorial that also tackles some of the toughest and most common problems associated with building duplex services in WCF.

The source code of the article can be downloaded here.

13 thoughts on “Building Duplex Services with WCF

  1. jan

    Hey Derek,

    Nice Tutorial, but the link to the sourcecode isnt working 🙁

    Reply
    1. derek Post author

      Hi jan,

      Thanks for the compliment! Could you try downloading the source code again? I tried on multiple computers and had no issue. It is a ZIP file – So maybe your browser is blocking it. Let me know if you have success downloading the source.

      Reply
  2. Ken

    I like the article and the source code, one thing I have noticed is if the service is not running the client is locked up or will not start. (How to handle that would be a nice addition.
    Also one thing I do not understand – is it really possible to send to the same channel or should a new channel be created for each request ?

    Reply
    1. derek Post author

      On the topic of whether to cache a channel or to just cache a channel factory and create a new channel with each request… It is generally regarded as best practice to create a new channel with each request. Channels are inexpensive to create and not thread-safe. Individual channels can also time out or become faulted which will require aborting the channel and creating a new one. Channel factories are more stable (less likely to become faulted), expensive to create and they are thread-safe.

      Reply
  3. rahul Rathore

    well elaborated and explained .Thank ‘s Derek for putting this great effort.

    Reply
    1. derek Post author

      To readers who have not been able to download the source code: What problem are you specifically having? Other readers have been able to download the source files. I have tested on multiple computers and browsers and had no success in replicating any issue. Please let me know any details related to the problem. Thanks!

      Reply
  4. Flemming Kentved

    Finally an example that actually works out of the box. Thank you!!!!

    Reply
  5. Coby

    Thank you for this simple, but effective, example and very clear explanation! This article has been bookmarked. 🙂

    Reply
  6. Rob Yaggie

    Wow, Derek! Your fully working sample application was exactly what I needed. It downloaded and ran on the first attempt. I actually will be enhancing a VB.NET application so I spent the day duplicating your sample in VB.NET so that let me really dig in and appreciate all of the work you did in putting this together. I like the way you have organized the code and will be leveraging that too. I just wanted to give you a big thank you!

    BTW, my immediate use of this will actually be a 1 to 1 pairing between the client and service running on the same machine. The user of the client will have admin access but the client will not be run elevated. The service will contain all the code that requires elevation and will now handle those parts (very small portion of the overall program). This will help in my case because my client is a startup application and if I have it run elevated then the UAC prompt comes at a bad time (possible blocking or timeout issues, etc). Moving that code to the service allows the client to come up clean and a significant amount of work. Then I will be able to spin up (ex. process.start) the elevated service part at a more convenient time (ex. when my user starts interacting with the program) where the UAC prompt can be confirmed. I also feel that keeping a tighter reign on the elevated code will make it more secure. Additionally, I have some longer term plans in my applications that will use WCF in a more conventional way like in your sample so you’ve helped me with my “proof of concept” on multiple projects.

    Reply
  7. Thomas E Bell

    Truly a great article. Well written and concise. Probably the best example of writing a simple duplex service/client I have ever seen!

    Reply

Leave a Reply to Coby Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.