views:

864

answers:

1

I'm working on an application that uses WCF to communicate between server and clients Server has changing number of services, along with one master service that clients can query for information about other services, and then subscribe to those they want using information from master service.

Master service sends notifications to subscribed clients, when sub-services change (added, removed, etc) Also sub-services send notifications every few seconds.

This sounds like a great opportunity for using WCFs Callback channels, however, they require you to use session. Also when sending notifications to clients, when any of the cliens got disconnected disgracefully the service hangs, trying to notify it. Even if it times out, after some time, during that time no other client get's notified and this is not acceptable, since the clients are actually GUIs for monotoring applications behind sub-services. Also using MSMQ for that is not an option, since the service needs to know immediatelly, when send operation did not succeed.

The solution I came up with is using http binding (to know immediatelly when send did not succeed) and creating explicit callback services on the client, but that requires me to write a lot of ugly looking infrastructore code.

So, my question is: is there a better way of doing this with WCF. The best looking option was Callback Contracts, because that frees me from needing to manage callback service manually, if only it could not wait for ack from one client before trying to send to the next one.

+2  A: 

I have a similar environment (without the dynamic services) and had a very similar issue when client channels faulted. The first solution I came up with was to wrap the callbacks in try/catch statements and remove the offending client if something went wrong but this had issues and didn't look like it would scale at all.

The solution I ended up going with was to use a delegated event handler and to call it using BeginInvoke. If you haven't looked at the CodeProject: WCF/WPF Chat Application (Chatters) solution, I recommend checking it out.

When a user logs in, an event handler is created and added to the main event:

public bool Login() 
{
    ...
    _myEventHandler = new ChatEventHandler(MyEventHandler);
    ChatEvent += _myEventHandler;
    ...
}

Whenever a message needs to be broadcast, the event handlers are called asynchronously:

private void BroadcastMessage(ChatEventArgs e)
{
    ChatEventHandler temp = ChatEvent;
    if (temp != null)
    {
        foreach (ChatEventHandler handler in temp.GetInvocationList())
        {
            handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
        }
    }
}

When the returns come back, the result is handled and if something bad happened the event handler for that channel is removed:

private void EndAsync(IAsyncResult ar)
{
    ChatEventHandler d = null;
    try
    {
        //get the standard System.Runtime.Remoting.Messaging.AsyncResult,and then
        //cast it to the correct delegate type, and do an end invoke
        System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
        d = ((ChatEventHandler)asres.AsyncDelegate);
        d.EndInvoke(ar);
    }
    catch(Exception ex)
    {
        ChatEvent -= d;
    }
}

The code above is modified (slightly) from the WCF/WPF Chat Application posted by Sacha Barber.

Chris Porter