Making WCF Proxy useable
The way WCF proxies are designed is to live until shi* happens.
Let’s assume that we have a CalcualtorService with one method named Divide(int a, int b). Sasha, a cool programmer-dude, trying to produce some usefull software writes:
public MyCalcualtorForm : Form {
private CalculatorProxy _calc = new CalculatorProxy();
Calc_Click(…) {
_calc.Divide(firstNumber, secondNumber);
}
}
What is the first error you can think of that could happen? Yep, DivideByZeroException.
Once the proxy gets an exception, it enters into a “Faulted” state which makes the proxy unusable(=you cannot use it again).
The quickest solution is to work “by the book” and create a new instance each and every time we need to call the service:
Calc_Click(…) {
using (CalculatorProxy calc = new CalculatorProxy())
calc.Divide(firstNumber, secondNumber);
}
But what’s bad in this solution?
- Performance – you pay (not a lot but neither little) for each creation of the proxy. Sure, it will probably not be your bottleneck, but heck, why is it useful? Most of the time the proxy will not throw an exception and yet we need to create it every time just to avoid the faulted state scenario.
- Design – If we declare this exception BY CONTRACT, I would expect that the proxy will still be usable afterwards. Do we really want to return Enum\int\string as status instead of throwing exception just because of poor design?
- TDD – you know that I’m in love with it. Now imagine Dependency Injection. Component A recieve ICalculatorProxy and use it to… calculate something. Working “by the book” is no good as we want to recieve an instance of the proxy from the outside in order to mock it. Right, so we inject a proxy from the outside (got to love Windsor) and life is pretty sweeet. Darn! Wait! one poor (even by design) exception and our proxy goes dead. Very un-TDDish of Microsoft.
I had to come with a solution as no one will take TDD away from me. I present to you ProtectedProxy: this little IL-code-at-the-end-of-the-day will able you to recover from faulted state by creating a new proxy on each exception thus making your proxy… useable (couldn’t think about a better word to describe it). Think about a situation where your proxy is trying to call the service but the service is down; In Semingo, we decided that we want to keep trying until the service is up. Via ProtectedProxy, you can determine how many times do you want to recover and when you should finally kill the proxy. Oh yea, ProtectedProxy uses Windsor in order to create new proxies if needed and logging messages to log4net. Good stuff.
In the example above, all Sasha had to do was to:
1). Initialize the _calc field by:
ProtectedProxy<ICalculatorProxy> _calc = new ProtectedProxy<ICalculatorProxy>(new CalculatorProxy());
2). call _calc via:
_calc.Instance.Divide(firstNumber, secondNumber);
But enough said, code please:
// Written by Oren Ellenbogen (07.08.07) – trying to protect our proxies so they could recover from:
// (A) The service is not up yet, but we want to try again later.
// (B) The service throws (ANY) exception, we still want our proxy to function (bubble the exception, but still keep on working).
// Microsoft intended to use a NEW proxy per call, but for TDD this is not ideal as we would like to inject proxies from outside as mocks
// in order to simulate multiple scenarios.
#region using
using System;
using System.Reflection;
using System.ServiceModel;
using Castle.Core.Resource;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
using log4net;
#endregion
namespace Semingo.Services.Proxies.Helpers
{
public interface IProxy : ICommunicationObject {
bool Ping();
}
/// Protect proxy from entering Faulted state by re-creating the proxy via Windsor Container on Faulted.
/// IMPORTANT: that even if a fatal exception is raised by the service (for example: the service is not up yet), the proxy will be raised again.
/// Use it wisely (TIP: you CAN determine the number of ‘recovery’ attempts).
/// </summary>
/// <typeparam name=”I”>The proxy interface to protect</typeparam>
/// <remarks>
/// The way WCF works is that ANY exception on the service will cause the proxy to enter “faulted” state which means you can not use it anymore.
/// Imagine a service of CalculatorService that expose the method float Divide(int a, int b). Sending b=0 will raise an exception in the service
/// and the proxy will get into faulted state. This is not ideal as the proxy itself should be used again.
/// </remarks>
public class ProtectedProxy<I> : IDisposable
where I : IProxy
{
private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private I _instance;
private readonly IWindsorContainer _container;
private int _faultedCounter = 0;
private bool _disposed = false;
private const int AlertableNumberOfFaultedTimes = 10;
public ProtectedProxy(I instance)
: this(CreateXmlBasedWindsorContainer(), instance)
{
}
public ProtectedProxy(IWindsorContainer container, I instance)
{
_container = container;
ShieldInstance(instance);
}
/// <summary>
/// Returns the number of faults this proxy had so far
/// </summary>
public int NumberOfFaults
{
get { return _faultedCounter; }
}
public I Instance
{
get
{
ThrowIfInstanceAlreadyDisposed();
if (_instance.State == CommunicationState.Faulted || _instance.State == CommunicationState.Closed || _instance.State == CommunicationState.Closing)
{
_logger.Warn(“Notice: The proxy state is invalid (“ + communicationObj.State + “). The Faulted event should have been raised and handle this state – this need to be checked.”);
HandleFaultedInstance();
}
return _instance;
}
}
public void Close()
{
Dispose();
}
private void ThrowIfInstanceAlreadyDisposed()
{
if (_disposed)
throw new ObjectDisposedException(“The protected proxy for the type: “ + _instance.GetType().FullName + ” was closed. Cannot return a live instance of this type.”);
}
private void ShieldInstance(I instance)
{
_instance = instance;
_instance.Faulted += delegate { HandleFaultedInstance(); };
}
private void HandleFaultedInstance()
{
ThrowIfInstanceAlreadyDisposed();
_faultedCounter++;
if (_faultedCounter >= AlertableNumberOfFaultedTimes)
_logger.Warn(“ALERT! The proxy for the type “ + _instance.GetType().FullName + ” got faulted for the “ + _faultedCounter + ” time. Recreating the proxy but we must verify if this is valid.”);
else if (_logger.IsDebugEnabled)
_logger.Debug(“Proxy for type “ + _instance.GetType().FullName + ” got faulted (current state: “ + ((ICommunicationObject)_instance).State + “) – recreating the proxy. Number of faulted instances so far: “ + _faultedCounter + “.”);
ProxyHelper.CloseProxy(_instance); // close current proxy
ShieldInstance(_container.Resolve<I>()); // re-create the proxy, faulted proxies are no good for further use.
}
private static IWindsorContainer CreateXmlBasedWindsorContainer()
{
try
{
return new WindsorContainer(new XmlInterpreter(new ConfigResource(“castle”)));
}
catch (Exception err)
{
_logger.Warn(“Unable to create xml based windsor container, using empty one.”, err);
return new WindsorContainer(); // for testing (the proxy will be mocked anyway).
}
}
#region IDisposable Members
///<summary>
///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///</summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
_logger.Info(“Attempting to dispose the protected proxy for the type: “ + _instance.GetType().FullName + “, disposed already? “ + _disposed);
if (_disposed) return;
if (disposing)
{
ProxyHelper.CloseProxy(_instance);
}
_disposed = true;
}
#endregion
}
public static class ProxyHelper
{
private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static void CloseProxy(object proxy)
{
if (proxy == null) return;
CloseProxy(proxy as ICommunicationObject);
}
/// <summary>
/// Close the proxy in a safe manner (will not throw exception)
/// </summary>
/// <param name=”proxy”>The proxy to close</param>
public static void CloseProxy(ICommunicationObject proxy)
{
if (proxy == null) return;
try
{
if (proxy.State == CommunicationState.Closing || proxy.State == CommunicationState.Closed || proxy.State == CommunicationState.Faulted)
proxy.Abort();
else
proxy.Close();
}
catch (CommunicationException)
{
proxy.Abort();
}
catch (TimeoutException)
{
proxy.Abort();
}
catch (Exception err)
{
_logger.Error(err);
proxy.Abort();
}
finally
{
proxy = null;
}
}
}
Hours of joy…
Almost forgot, on the next post – “How to TDD WCF code” – stay tuned…