Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception in "using" statement with WCF not closing connections properly. How does one close faulted WCF client connections or those with exceptions?

There are several questions on StackOverflow regarding closing WCF connections, however the highest ranking answers refers to this blog:

http://marcgravell.blogspot.com/2008/11/dontdontuse-using.html

I have a problem with this technique when I set a breakpoint at the server and let the client hang for more than one minute. (I'm intentionally creating a timeout exception)

The issue is that the client appears to "hang" until the server is done processing. My guess is that everything is being cleaned up post-exception.

In regard to the TimeOutException it appears that the retry() logic of the client will continue to resubmit the query to the server over and over again, where I can see the server-side debugger queue up the requests and then execute each queued request concurrently. My code wan't expecting WCF to act this way and may be the cause of data corruption issues I'm seeing.

Something doesn't totally add up with this solution.

What is the all-encompassing modern way of dealing with faults and exceptions in a WCF proxy?

like image 781
makerofthings7 Avatar asked Feb 19 '11 03:02

makerofthings7


2 Answers

Update

Admittedly, this is a bit of mundane code to write. I currently prefer this linked answer, and don't see any "hacks" in that code that may cause issues down the road.


This is Microsoft's recommended way to handle WCF client calls:

For more detail see: Expected Exceptions

try
{
    ...
    double result = client.Add(value1, value2);
    ...
    client.Close();
}
catch (TimeoutException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}
catch (CommunicationException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}

Additional information So many people seem to be asking this question on WCF that Microsoft even created a dedicated sample to demonstrate how to handle exceptions:

c:\WF_WCF_Samples\WCF\Basic\Client\ExpectedExceptions\CS\client

Download the sample: C# or VB

Considering that there are so many issues involving the using statement, (heated?) Internal discussions and threads on this issue, I'm not going to waste my time trying to become a code cowboy and find a cleaner way. I'll just suck it up, and implement WCF clients this verbose (yet trusted) way for my server applications.

Optional Additional Failures to catch

Many exceptions derive from CommunicationException and I don't think most of those exceptions should be retried. I drudged through each exception on MSDN and found a short list of retry-able exceptions (in addition to TimeOutException above). Do let me know if I missed an exception that should be retried.

Exception   mostRecentEx = null;
for(int i=0; i<5; i++)  // Attempt a maximum of 5 times 
{
    try
    {
       ...
       double result = client.Add(value1, value2);
       ...
       client.Close();
    }

    // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
    catch (ChannelTerminatedException cte)
    {

      mostRecentEx = cte;
      secureSecretService.Abort();
        //  delay (backoff) and retry 
        Thread.Sleep(1000 * (i + 1)); 
    }

    // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
    // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
    catch (EndpointNotFoundException enfe)
    {

      mostRecentEx = enfe;
     secureSecretService.Abort();
        //  delay (backoff) and retry 
        Thread.Sleep(1000 * (i + 1)); 
    }

    // The following exception that is thrown when a server is too busy to accept a message.
    catch (ServerTooBusyException stbe)
    {
      mostRecentEx = stbe;
        secureSecretService.Abort();

        //  delay (backoff) and retry 
        Thread.Sleep(1000 * (i + 1)); 
    }

    catch(Exception ex)
    { 
         throw ex;  // rethrow any other exception not defined here
    }
}
if (mostRecentEx != null) 
{
    throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
}
like image 94
makerofthings7 Avatar answered Oct 07 '22 12:10

makerofthings7


Closing and Disposing a WCF Service

As that post alludes to, you Close when there were no exceptions and you Abort when there are errors. Dispose and thus Using shouldn't be used with WCF.

like image 39
Avilo Avatar answered Oct 07 '22 12:10

Avilo