Wednesday, August 22, 2007

using (wcfclient) { } kaboom!

I've been doing a lot of WCF development lately (love it) and ran into a bit of a stumbling block. The proxy client generated by Visual Studio 2008 Beta 2 doesn't handle fault conditions and dispose well. I have to admit, I haven't used WCF in Visual Studio 2005. If you're impatient here's the project (Visual Studio 2008 Beta 2 project): wcfwrapper.zip The situation unfolds:

The Problem - Dispose Isn't Safe!

  1. Create a WCF service.
    [ServiceContract]
    public interface IGoodBadService
    {
    [OperationContract]
    void Good();

    [OperationContract]
    void Bad();
    }
  2. Add a service reference to your service in another project.
  3. Create a service client and use it, with a "using" block (it is IDisposable, afterall).
    try
    {
    using (GoodBadServiceClient c =
    new GoodBadServiceClient())
    {
    c.Bad();
    }
    }
    catch (Exception ex)
    {
    Console.WriteLine(ex.Message);
    }
  4. A call to your service, for whatever reason, throws an exception.
    public class GoodBadService
    : IGoodBadService
    {
    public void Good()
    {
    throw new Exception("GOOD!");
    }

    public void Bad()
    {
    throw new Exception("BAD!");
    }
    }
  5. You get a cryptic System.ServiceModel.CommunicationObjectFaultedException instead of the real exception when using calls Dispose() on your proxy client. It reads: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

The Solution - A Simple Generic Wrapper

public class ServiceProxy<TClient>
: IDisposable
where TClient : ICommunicationObject
{
public ServiceProxy(TClient client)
{
Client = client;
}

public TClient Client { get; private set; }

public void Dispose()
{
if (null != Client &&
Client.State != CommunicationState.Closed &&
Client.State != CommunicationState.Faulted)
{
Client.Close();
}
}
}

Using the wrapper is pretty straightforward, but a tad more cryptic than just using the client directly. Some of this can be avoided with a generic factory.

So, here's how you use it:

try
{
using (ServiceProxy<GoodBadServiceClient> c
= new ServiceProxy<GoodBadServiceClient>(
new GoodBadServiceClient()))
{
c.Client.Good();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Run the project and check out the results. The wrapper gives you the actual exception, whereas "using" the client directly causes the CommunicationObjectFaultedException. Here's the project (Visual Studio 2008 Beta 2 project): wcfwrapper.zip

0 comments:

Post a Comment

About the Author

JD Conley is an entrepreneur and hacker, currently working away his golden handcuffs at Playdom, a subsidiary of the Walt Disney Company, since Hive7 was acquired. We make social games. The views and opinions expressed on this post are his and do not necessarily represent or reflect those of The Walt Disney Company.