Recover from a WCF Service Fault, Part 2 (Generic ServiceClientFactory Class)

After finding the simple solution for handling WCF Service Faults in the original post, I figured it should be relatively trivial to find a generic solution to this problem if you’re using similar WCF clients in a project and want to reset ALL of them on a channel fault.  I developed a generic ServiceClientFactory class that will generate a WCF Service Client instance from a generic “GetClient” function and will automatically handle resetting of faulted channels.

Class Implementation Source Code:

/* Service Client Factory
 * Author: Michael Gerety, Senior Consultant, Tallan, Inc.
 * Description: A generic service client factory that automatically
 *              resets faulted channels for WCF services that have
 *              endpoints and behaviors defined in web/app.config files.
 */

using System;
using System.Collections.Generic;
using System.ServiceModel;
namespace Tallan
{
    /// <summary>
    /// Singleton factory class for WCF Services.
    /// Creates service clients based on interface type and automatically
    /// resets faulted channels.
    /// </summary>
    public class ServiceClientFactory
    {
        private readonly Dictionary<Type, object> factories;
        private static ServiceClientFactory instance;

        private ServiceClientFactory()
        {
            factories = new Dictionary<Type, object>();
        }

        /// <summary>
        /// Retrieves a service client for the interface specified in generic parameter.
        /// </summary>
        /// <typeparam name="T">Interface type to use for Service Client creation.</typeparam>
        /// <returns>Service client instance for specified interface.</returns>
        public T GetClient<T>()
        {
            var genericType = typeof(T);
            Type serviceClientType;
            if (genericType.IsInterface)
            {
                serviceClientType = GetClientType(genericType);

                if (serviceClientType == null)
                    return default(T);

                var client = Activator.CreateInstance(serviceClientType);
                if (!(client is ICommunicationObject))
                {
                    client = null;
                    return (T)client;

                }
                (client as ICommunicationObject).Faulted += Channel_Faulted<T>;

                if (!factories.ContainsKey(typeof(T)))
                {
                    var prop = serviceClientType.GetProperty("ChannelFactory");
                    var factory = prop.GetValue(client, null);
                    factories.Add(typeof(T), factory);
                }
                return (T)client;
            }
            return default(T);
        }

        #region Reflection Utilities
        private static Type GetClientType(Type type)
        {
            var assy = type.Assembly;
            var serviceModelAssy = typeof(ChannelFactory).Assembly;
            var clientBaseType = serviceModelAssy.GetType("System.ServiceModel.ClientBase`1").MakeGenericType(type);

            foreach (var classType in assy.GetTypes())
            {
                if (classType.IsClass && type.IsAssignableFrom(classType))
                {
                    if (classType.IsSubclassOf(clientBaseType))
                        return classType;
                }
            }

            return null;
        }

        #endregion

        /// <summary>
        /// Event handler for ClientBase.Faulted event.
        /// </summary>
        /// <typeparam name="T">Interface type of service</typeparam>
        /// <param name="sender">ClientBase instance</param>
        /// <param name="e">Event Args</param>
        private void Channel_Faulted<T>(object sender, EventArgs e)
        {
            ((ICommunicationObject)sender).Abort();
            var factory = (ChannelFactory<T>)factories[typeof(T)];
            factory.CreateChannel();
        }

        /// <summary>
        /// Returns the singleton instance of ServiceClientFactory.
        /// </summary>
        /// <returns>Singleton instance of ServiceClientFactory</returns>
        public static ServiceClientFactory GetFactory()
        {
            if (instance == null)
            {
                instance = new ServiceClientFactory();
            }
            return instance;
        }
    }
}

Sample Usage:

//Get instance of ServiceClientFactory
            var factory = ServiceClientFactory.GetFactory();
            var client = factory.GetClient<IAuthenticateService>();
            try
            {
                client.AuthenticateUser("joe", "bob");
            }
            catch (Exception)
            {
                //Handle Exception
            }

            //channel isn't in faulted state, you can re-execute.
            client.AuthenticateUser("joe", "bob1");

This was thrown together and proven to work for my purposes.  If anyone has any suggestions about how to improve this utility class, please feel free to comment or contact me with suggestions.

This entry was posted in .NET Framework, WCF and tagged , , , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

7 Comments

  1. Robert Sullivan
    Posted September 15, 2009 at 6:49 pm | Permalink

    Thanks for the above code, it is well written and easy to understand, and was exactly what I was looking for!

    I do have one thought if I may…

    I have a service client with multiple endpoints defined in web/app.config. Using the GetClient() method will cause an exception to be thrown because WCF can’t decide for me which endpoint to use. I edited the above method and added a new one as follows:

    }

    ///
    /// Retrieves a service client for the interface specified in generic parameter.
    ///
    /// Interface type to use for Service Client creation.
    /// Service client instance for specified interface.
    public T GetClient()
    {
    // Passing null causes reflection to use the default constructor of the Service Client (This works if only one endpoint is defined for the service)
    return GetClient(null);
    }

    ///
    /// Retrieves a service client for the interface specified in generic parameter.
    ///
    /// Interface type to use for Service Client creation.
    /// Object array of arguments to pass to the Service Client’s constuctor
    /// Service client instance for specified interface.
    /// The ‘ values must match the order and type of a constructor for the Service Client.
    public T GetClient(params object[] constructorArgs)
    {
    var genericType = typeof(T);
    Type serviceClientType;
    if (genericType.IsInterface)
    {
    serviceClientType = GetClientType(genericType);

    if (serviceClientType == null)
    return default(T);

    // Pass the supplied constructor arguments to the CreateInstance method
    var client = Activator.CreateInstance(serviceClientType, constructorArgs);
    if (!(client is ICommunicationObject))
    {
    client = null;
    return (T)client;

    }
    (client as ICommunicationObject).Faulted += Channel_Faulted;

    if (!factories.ContainsKey(typeof(T)))
    {
    var prop = serviceClientType.GetProperty(“ChannelFactory”);
    var factory = prop.GetValue(client, null);
    factories.Add(typeof(T), factory);
    }
    return (T)client;
    }
    return default(T);
    }

    For my personal situation, I only need to pass the endpointConfigurationName as a constructor parameter, but I think this implementation will allow for any of the service client’s constructors to be called.

    I’ve tested it in a limited fashion, but wanted to see if you had any comments/concerns.

    Once again thanks for pointing me in the right direction.

  2. mgerety
    Posted September 16, 2009 at 10:58 am | Permalink

    @Robert:
    I’m glad the code was able to help you out.

    I hadn’t thought about your issue as the services I was using this for are pretty simple, however your solution seems to work nicely. I may add something like this into the next iteration.

    Thanks for submitting your additions to it!

  3. Posted January 14, 2010 at 7:08 am | Permalink

    Thanks you very much for this wonderfull piece of code. I was looking for a solution to this dreaded issue (faulted commmunication state) when i found this article. We are now adding some error reporting. If you are interessted in seeing the revised version, just let me kow and i’ll send it to you when we have it.

    Kind regards,

    Michel

  4. Posted August 5, 2010 at 7:57 am | Permalink

    Thanks for this code! I was creating something like this myself but you fixed it nicely!

    Their is one situation i worry about:
    -Channel is created
    -Channel faults
    -Event is fired to recover the channel
    -Appication tries to use the channel (before its recreated)
    -Channel is recreated

    Is their a good way to get around this?

  5. Posted August 5, 2010 at 9:15 am | Permalink

    Secondly I cannot get this work with non-generated proxy’s.
    They don’t implement ‘System.ServiceModel.ClientBase’.

    Another thing I don’t understand:
    private void Channel_Faulted(object sender, EventArgs e)
    {
    ((ICommunicationObject)sender).Abort();
    var factory = (ChannelFactory)factories[typeof(T)];
    factory.CreateChannel();
    }

    If the factory creates a new channel like this, is the referenced channel refreshed? I don’t see how the new channel updates the faulted one.

  6. tivivi
    Posted November 26, 2010 at 12:38 pm | Permalink

    Nice code. Thanks.

    But what about DuplexClientBase? Does the following version match?
    private static Type GetProxyType(Type type)
    {
    var assy = type.Assembly;
    var serviceModelAssy = typeof(ChannelFactory).Assembly;
    var clientBaseType = serviceModelAssy.GetType(“System.ServiceModel.ClientBase`1″).MakeGenericType(type);
    var duplexClientBaseType = serviceModelAssy.GetType(“System.ServiceModel.DuplexClientBase`1″).MakeGenericType(type);

    foreach (var classType in assy.GetTypes())
    {
    if (classType.IsClass && type.IsAssignableFrom(classType))
    {
    if (classType.IsSubclassOf(clientBaseType) || classType.IsSubclassOf(clientBaseType))
    return classType;
    }
    }

    return null;
    }

  7. tivivi
    Posted November 26, 2010 at 12:39 pm | Permalink

    if (classType.IsSubclassOf(clientBaseType) || classType.IsSubclassOf(duplexClientBaseType))

    sorry :)

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*