This is a follow up to my previous (in fact, my first) post “Mmmmm… clean, sweet WCF proxy usage with TidyProxy!”, which showed how to safely use an arbitrary WCF proxy class safely.
A substantial part of my day-to-day development is working with WCF services, mainly web and MSMQ and after a few teething troubles, everything’s been ticking along nicely. Generally, these are internal windows->web comm’s where I can control both client and server implementations, so lots of shared types.
However, one of my fastest ever growing webservice APIs recently hit a metadata limit (apparently, there’s a reason for the up to 30 methods guideline!) which meant that neither VS2008 nor svcutil could reliable generate usable proxies without altering the standard configuration of every machine which may need to generate proxies.
So after a monster refactoring session to break down my one service into fourteen (14! – how did it get that big!) smaller, more cohesive services – I’m faced with an even more mammoth task of retrofitting the smaller services in place of the larger one. Clearly, this was not going to be pretty as we’ll need to replace the one service reference with fourteen! As this service is used across several projects, this could result in me being nailed to a wall.
After a bit a Googling, Reflectoring and inspecting the Generated Code, it quickly became apparent that for my case, I didn’t need to be generating proxies at all! As, all the types were shared between client and server using common libraries and the WCF configuration in config files, I could just wrap the ChannelFactory<>
. This clever class does all the heavy lifting in WCF and only needs the service contract interface and an endpoint name!
As if by Magic, the Proxy Appeared…
using System;
using System.ServiceModel;
namespace MartinOnDotNet.Helpers.WCF
{
public class DynamicTidyProxy<TServiceContract> : IDisposable
{
public DynamicTidyProxy()
{
EndpointName = typeof(TServiceContract).Name;
}
public DynamicTidyProxy(string endpointConfigurationName)
{
EndpointName = endpointConfigurationName;
}
public string EndpointName { get; set; }
private ChannelFactory<TServiceContract> _channel;
private TServiceContract _client;
public TServiceContract Client
{
get
{
if (_client == null)
{
if (!typeof(TServiceContract).IsInterface)
throw new NotSupportedException("TServiceContract must be an interface!");
if (string.IsNullOrEmpty(EndpointName))
throw new NotSupportedException("EndpointName must be set prior to use!");
_channel = new ChannelFactory<TServiceContract>(EndpointName);
_client = _channel.CreateChannel();
}
return _client;
}
}
#region IDisposable Members
public void Dispose()
{
if (_channel != null)
{
_channel.CloseConnection();
}
GC.SuppressFinalize(this);
}
#endregion
}
}
I’ve coded the default constructor to use the service contract interface type name… so make sure your config file matches!
You can then dynamically create WCF proxies in code using:
using (var proxy = new DynamicTidyProxy<IMagicComplexServiceContract>())
{
proxy.Client.DoSomeMagicallyComplexOperation();
}
No svcutil, or VS2008 Service Reference Required!
It also means that I didn’t have to spend all that time refactoring! (today at least…it was going to happen - promise).
Tidying Up
You’ve probably noticed the subtle “_channel.CloseConnection()
” in the DynamicTidyProxy Dispose
method…this is simply an extension method that wraps all of the required WCF boiler plate code…
public static void CloseConnection(this ICommunicationObject serviceClient)
{
if (serviceClient == null) return;
try
{
if (serviceClient.State == CommunicationState.Opened)
{
serviceClient.Close();
}
else
{
serviceClient.Abort();
}
}
catch (CommunicationException ex)
{
Logging.Logger.Log(ex);
try
{
serviceClient.Abort();
}
catch { }
}
catch (TimeoutException ex)
{
Logging.Logger.Log(ex);
try
{
serviceClient.Abort();
}
catch { }
}
catch (Exception ex)
{
Logging.Logger.Log(ex);
try
{
serviceClient.Abort();
}
catch { }
throw;
}
}