Thursday, August 7, 2008

WCF Client Proxy IDisposable - Generic WCF Service Proxy

I have run into this issue on several clients now.  The basic issue is when using WCF on the client, using ClientBase<>, and you do not close the channel, you can tie up the server until the channel times out.  So, once the max instances, sessions, or concurrent calls is reached and the clients are not closing their channels, the server will block and queue up subsequent calls.  The un-closed client channels will eventually timeout, which causes a fault on the client, and the next set of calls will then make it through.

When I first hit this issue, my thought was to wrap my client base code in a using () statement so Dispose() would then be called.  But, ClientBase<> does not implement IDisposable.  Here is some info on the issue...

Guidance on factory close and message faults

Why does ClientBase Dispose need to throw on faulted state

So, after lots of testing to understand all the WCF knobs to tweak, I came up with a generic class I called ServiceProxy for clients to use when creating/using client channels.  This has been through several revisions and here is what I have ended up with.  The idea to add support for the delegate came from this blog entry

iServiceOriented.com

Here is the code for my generic service proxy wrapper...

public class ServiceProxy<TInterface> : ClientBase<TInterface>, IDisposable where TInterface : class

    {

        public delegate void ServiceProxyDelegate<T>(TInterface proxy);

 

        public ServiceProxy()

            : base(typeof(TInterface).ToString())

        {

        }

        public ServiceProxy(string endpointConfigurationName)

            : base(endpointConfigurationName)

        {

        }

 

        protected override TInterface CreateChannel()

        {

            return base.CreateChannel();

        }

 

        public TInterface Proxy

        {

            get

            {

                return this.Channel;

            }

        }

 

        public static void Call(ServiceProxyDelegate<TInterface> proxyDelegate)

        {

            Call(proxyDelegate, typeof(TInterface).ToString());

        }

 

        public static void Call(ServiceProxyDelegate<TInterface> proxyDelegate, string endpointConfigurationName)

        {

            ChannelFactory<TInterface> channel = new ChannelFactory<TInterface>(endpointConfigurationName);

 

            try

            {

                proxyDelegate(channel.CreateChannel());

            }

            finally

            {

                if (channel.State == CommunicationState.Faulted)

                {

                    channel.Abort();

                }

                else

                {

                    try

                    {

                        channel.Close();

                    }

                    catch

                    {

                        channel.Abort();

                    }

                }

            }

        }

 

        public void Dispose()

        {

            if (this.State == CommunicationState.Faulted)

            {

                base.Abort();

            }

            else

            {

                try

                {

                    base.Close();

                }

                catch

                {

                    base.Abort();

                }

            }

        }

    }

 

And, here are some usages samples...

//delegate example1

            string response = null;

            ServiceModel.ServiceProxy<IUnitTestService>.Call(p =>

            {

                response = p.DoStuff("ServiceProxyUsingTest");

            }

            );

 

            //delegate example2

            string response = null;

            ServiceProxy<IUnitTestService>.Call(p => response = p.DoStuff("ServiceProxyUsingTest"));

 

            //using example

            string response = null;

            using (ServiceProxy<IUnitTestService> service = new ServiceProxy<IUnitTestService>())

            {

                response = service.Proxy.DoStuff("ServiceProxyUsingTest");

            }