What is the best workaround for the WCF client `using` block issue?

Viewed 115536

I like instantiating my WCF service clients within a using block as it's pretty much the standard way to use resources that implement IDisposable:

using (var client = new SomeWCFServiceClient()) 
{
    //Do something with the client 
}

But, as noted in this MSDN article, wrapping a WCF client in a using block could mask any errors that result in the client being left in a faulted state (like a timeout or communication problem). Long story short, when Dispose() is called, the client's Close() method fires, but throws an error because it's in a faulted state. The original exception is then masked by the second exception. Not good.

The suggested workaround in the MSDN article is to completely avoid using a using block, and to instead instantiate your clients and use them something like this:

try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

Compared to the using block, I think that's ugly. And a lot of code to write each time you need a client.

Luckily, I found a few other workarounds, such as this one on the (now defunct) IServiceOriented blog. You start with:

public delegate void UseServiceDelegate<T>(T proxy); 

public static class Service<T> 
{ 
    public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>(""); 
    
    public static void Use(UseServiceDelegate<T> codeBlock) 
    { 
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel(); 
        bool success = false; 
        try 
        { 
            codeBlock((T)proxy); 
            proxy.Close(); 
            success = true; 
        } 
        finally 
        { 
            if (!success) 
            { 
                proxy.Abort(); 
            } 
        } 
     } 
} 

Which then allows:

Service<IOrderService>.Use(orderService => 
{ 
    orderService.PlaceOrder(request); 
}); 

That's not bad, but I don't think it's as expressive and easily understandable as the using block.

The workaround I'm currently trying to use I first read about on blog.davidbarret.net. Basically, you override the client's Dispose() method wherever you use it. Something like:

public partial class SomeWCFServiceClient : IDisposable
{
    void IDisposable.Dispose() 
    {
        if (this.State == CommunicationState.Faulted) 
        {
            this.Abort();
        } 
        else 
        {
            this.Close();
        }
    }
}

This appears to be able to allow the using block again without the danger of masking a faulted state exception.

So, are there any other gotchas I have to look out for using these workarounds? Has anybody come up with anything better?

26 Answers

Actually, although I blogged (see Luke's answer), I think this is better than my IDisposable wrapper. Typical code:

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
}); 

(edit per comments)

Since Use returns void, the easiest way to handle return values is via a captured variable:

int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
  {
    newOrderId = orderService.PlaceOrder(request);
  });
Console.WriteLine(newOrderId); // should be updated

I wrote a higher order function to make it work right. We've used this in several projects and it seems to work great. This is how things should have been done from the start, without the "using" paradigm or so on.

TReturn UseService<TChannel, TReturn>(Func<TChannel, TReturn> code)
{
    var chanFactory = GetCachedFactory<TChannel>();
    TChannel channel = chanFactory.CreateChannel();
    bool error = true;
    try {
        TReturn result = code(channel);
        ((IClientChannel)channel).Close();
        error = false;
        return result;
    }
    finally {
        if (error) {
            ((IClientChannel)channel).Abort();
        }
    }
}

You can make calls like this:

int a = 1;
int b = 2;
int sum = UseService((ICalculator calc) => calc.Add(a, b));
Console.WriteLine(sum);

This is pretty much just like you have in your example. In some projects, we write strongly typed helper methods, so we end up writing things like "Wcf.UseFooService(f=>f...)".

I find it quite elegant, all things considered. Is there a particular problem you encountered?

This allows other nifty features to be plugged in. For instance, on one site, the site authenticates to the service on behalf of the logged in user. (The site has no credentials by itself.) By writing our own "UseService" method helper, we can configure the channel factory the way we want, etc. We're also not bound to using the generated proxies -- any interface will do.

I've finally found some solid steps towards a clean solution to this problem.

This custom tool extends WCFProxyGenerator to provide an exception handling proxy. It generates an additional proxy called ExceptionHandlingProxy<T> which inherits ExceptionHandlingProxyBase<T> - the latter of which implements the meat of the proxy's functionality. The result is that you can choose to use the default proxy that inherits ClientBase<T> or ExceptionHandlingProxy<T> which encapsulates managing the lifetime of the channel factory and channel. ExceptionHandlingProxy respects your selections in the Add Service Reference dialog with respect to asynchronous methods and collection types.

Codeplex has a project called Exception Handling WCF Proxy Generator. It basically installs a new custom tool to Visual Studio 2008, then use this tool to generate the new service proxy (Add service reference). It has some nice functionality to deal with faulted channels, timeouts and safe disposal. There's an excellent video here called ExceptionHandlingProxyWrapper explaining exactly how this works.

You can safely use the Using statement again, and if the channel is faulted on any request (TimeoutException or CommunicationException), the Wrapper will re-initialize the faulted channel and retry the query. If that fails then it will call the Abort() command and dispose of the proxy and rethrow the Exception. If the service throws a FaultException code it will stop executing, and the proxy will be aborted safely throwing the correct exception as expected.

Our system architecture often uses the Unity IoC framework to create instances of ClientBase so there's no sure way to enforce that the other developers even use using{} blocks. In order to make it as fool-proof as possible, I made this custom class that extends ClientBase, and handles closing down the channel on dispose, or on finalize in case someone doesn't explicitly dispose of the Unity created instance.

There is also stuff that needed to be done in the constructor to set up the channel for custom credentials and stuff, so that's in here too...

public abstract class PFServer2ServerClientBase<TChannel> : ClientBase<TChannel>, IDisposable where TChannel : class
{
    private bool disposed = false;

    public PFServer2ServerClientBase()
    {
        // Copy information from custom identity into credentials, and other channel setup...
    }

    ~PFServer2ServerClientBase()
    {
        this.Dispose(false);
    }

    void IDisposable.Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            try
            {
                    if (this.State == CommunicationState.Opened)
                        this.Close();
            }
            finally
            {
                if (this.State == CommunicationState.Faulted)
                    this.Abort();
            }
            this.disposed = true;
        }
    }
}

Then a client can simply:

internal class TestClient : PFServer2ServerClientBase<ITest>, ITest
{
    public string TestMethod(int value)
    {
        return base.Channel.TestMethod(value);
    }
}

And the caller can do any of these:

public SomeClass
{
    [Dependency]
    public ITest test { get; set; }

    // Not the best, but should still work due to finalizer.
    public string Method1(int value)
    {
        return this.test.TestMethod(value);
    }

    // The good way to do it
    public string Method2(int value)
    {
        using(ITest t = unityContainer.Resolve<ITest>())
        {
            return t.TestMethod(value);
        }
    }
}
Related