views:

219

answers:

2

I'm making a small chat application. I got to the point where one client sends a message, and then by using callbacks of other clients in the room, they quickly recieve the message. So, if there are 10 people in the room, the service instance of the client who is sending the message, will invoke this in 10 threads:

targetCallback.RecieveMessage(message);

Now, internet connections are volatile, and it could just so happen that it breaks a moment before that's performed, so an exception would be thrown and the client instance automatically destroyed, leaving the message unsent or half-successful.

I found examples that don't really handle those exceptions at all, while other put a simple:

try
{
    targetCallback.RecieveMessage(message);
}
catch
{
}

But, is that the best way to handle this exception?

+1  A: 

See

http://msdn.microsoft.com/en-us/library/aa354510.aspx

for good advice. Briefly, you should catch TimeoutException and CommunicationException.

Brian
That's good advice, but as ligos noted, other exceptions can occur. Quick answer would be, add ObjectDisposedExceptions to the list as well... but what guarantees do I have that callbacks can throw "only" those 3 exceptions?
avance70
If i'm reading it correctly, I should only catch those 2 exeptions, and try my best to programmatically account for all others.
avance70
There's no way of knowing what exceptions the internal WCF code could throw. However, most WCF related exceptions are derived from CommunicationsException, so that covers most. Timeout and Disposed are not in that hierarchy. I only found the DisposedException after I ran my application for a few days and got a few unexpected exceptions that I thought I could handle.
+1  A: 

I've also seen ObjectDisposedExceptions when calling WCF callbacks, and maybe a SecurityException of some sort, but I can't remember which one.

For applications that need a higher degree of reliability, I write a basic wrapper method that catches all the exceptions and removes that callback from your callback collection. Then I only ever invoke the callback through the wrapper.

Normally I'd also capture some contextual information about the conneciton from the OperationContext like IP address, sessionID and user token so I can identify who failed and log it. But that's left as an exercise to the reader.

public interface ICallbackContract
{
    void Operation(string arg);
}

class Program
{
    private List<ICallbackContract> Callbacks;

    private void SendMessage(string msg)
    {
        lock (this.Callbacks)
        {
            foreach (var callback in this.Callbacks)
            {
                this.InvokeWcf(callback, (c) => c.Operation(msg));
            }
        }
    }

    public void InvokeWcf(this ICallbackContract contract, Action<ICallbackContract> op)
    {
        if (((ICommunicationObject)contract).State != CommunicationState.Opened)
        {
            lock (this.Callbacks)
                Callbacks.Remove(contract);
            myLogger.LogError("That contract isn't open! Disconnected.");
            return;
        }

        try
        {
            op(contract);
        }
        catch (TimeoutException ex)
        {
            lock (this.Callbacks)
                Callbacks.Remove(contract);
            myLogger.LogError("That contract timed out! Disconnected.", ex);
            return;
        }
        catch (CommunicationException ex)
        {
            ...
        }
        catch (ObjectDisposedException ex)
        {
            ...
        }
        catch (Exception ex)
        }
            // Unexpected case.
            lock (this.Callbacks)
                Callbacks.Remove(contract);
            myLogger.FatalError("Something really bad happened!.", ex);
            throw;
        {
    }

}
What Brian pointed out, seems like a good solution... it would seem that, after reading the text he provided, ObjectDisposedExceptions would fall to the "Unexpected Exception" group, describing further: "Typically there is no useful way to handle unexpected errors, so typically you should not catch them when calling a WCF client communication method."
avance70
I don't think ObjectDisposed falls into the Unexpected category: that means the connection isn't valid any more because it's been closed, and removing it from your list of callbacks is an appropriate strategy. A Disposed exception can be safely recovered from, and probably shouldn't crash the app. But I did forget to show the unexpected case in my example; edited appropriately.