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.