Wednesday, March 2, 2011

Retring WCF calls, using Func

Sometimes because the internet connection is not great, you might want to implement code to retry WCF calls.  However, instead of writing for loops on each clientproxy.MethodCall, you can write an extension method that retries it for you.  Since WCF calls has a single parameter - TWcfRequest and a single response - TWcfResponse, we can use the delegate Func<TWcfRequest, TWcfResponse> and pass the desired WCF method down this extension method.


public static class WcfClientBaseExtension
    {
        /// <summary>
        /// Retry WCF call multiple times
        /// </summary>
        /// <typeparam name="TChannel"></typeparam>
        /// <typeparam name="TWcfRequest"></typeparam>
        /// <typeparam name="TWcfResponse"></typeparam>
        /// <param name="client"></param>
        /// <param name="tryExecute"></param>
        /// <param name="wcfRequest"></param>
        /// <returns></returns>
        public static TWcfResponse ExecuteRepeatedly<TChannel, TWcfRequest, TWcfResponse>(this ClientBase<TChannel> client,
                    Func<TWcfRequest, TWcfResponse> tryExecute, TWcfRequest wcfRequest)
            where TChannel : class
            where TWcfRequest : class
            where TWcfResponse : class
        {
            int maxRetryCount = FrameworkConfigHelper.MaxWcfRetryCount;

            for (int currentRetryCount = 0; currentRetryCount < maxRetryCount; currentRetryCount++)
            {
                try
                {
                    return tryExecute(wcfRequest);
                }
                //SOAP Faults - business requirement
                catch (FaultException)
                {
                    //need to allow business exceptions to go to top level
                    throw;
                }
                //Connection timeout
                catch (EndpointNotFoundException)
                {
                    //endpoint don't respond in timely manner
                    //swallow technical exceptions unless it's the last retry
                    if (currentRetryCount == maxRetryCount - 1)
                        throw;
                }
                catch (Exception)
                {
                    //rethrow all other errors for now
                    throw;
                }
            }
            return null;
        }

       
    }


For usage:
var searchResponse = client.ExecuteRepeatedly<ICollateralRegistrationSearch, SearchByRegistrationNumberRequestType, SearchByRegistrationNumberResponseType>
                                            (client.SearchByRegistrationNumber, searchByRegistrationNumberRequestType);