Search code examples
c#.netwcfchannelfactory

Dispose Channel created by WCF ChannelFactory


I'm looking for a clean way to have the ChannelFactory create channels for me with the ability to dispose them after use.
This is what I got:

public class ClientFactory : IClientFactory
{
    private const string endpointName = "IMyService";
    private readonly ChannelFactory<IMyService> _factory;

    public ClientFactory()
    {
        _factory = new ChannelFactory<IMyService>(endpointName);
    }

    public Client<IMyService> GetClient()
    {       
        IMyService channel = _factory.CreateChannel();
        return new Client<IMyService>(channel);
    }
}

public class Client<T> : IDisposable
{
    public T Channel { get; private set; }

    public Client(T channel)
    {
        if (channel == null)
            throw new ArgumentException("channel");

        Channel = channel;
    }

    public void Dispose()
    {
        (Channel as IDisposable).Dispose();
    }
}

//usage
using (var client = _serviceFactory.GetClient())
{
    client.Channel.DoStuff();
}

Is this a good solution?
Are there cleaner ways to do this?


Solution

  • No, there is no cleaner way to wrap the channel.

    Another way you can do it is to use Action/Func instead. It is not cleaner but might be more suitable for your application.

    This is how I do it:

    internal class WrappedClient<T, TResult> : IDisposable
    {
        private readonly ChannelFactory<T> _factory;
        private readonly object _channelLock = new object();
        private T _wrappedChannel;
    
        public WrappedClient(ChannelFactory<T> factory)
        {
            _factory = factory;
        }
    
        protected T WrappedChannel
        {
            get
            {
                lock (_channelLock)
                {
                    if (!Equals(_wrappedChannel, default(T)))
                    {
                        var state = ((ICommunicationObject)_wrappedChannel).State;
                        if (state == CommunicationState.Faulted)
                        {
                            // channel has been faulted, we want to create a new one so clear it
                            _wrappedChannel = default(T);
                        }
                    }
    
                    if (Equals(_wrappedChannel, default(T)))
                    {
                        _wrappedChannel = _factory.CreateChannel();
                    }
                }
    
                return _wrappedChannel;
            }
        }
    
        public TResult Invoke(Func<T, TResult> func)
        {
            try
            {
                return func(WrappedChannel);
            }
            catch (FaultException)
            {
                throw;
            }
            catch (CommunicationException)
            {
                // maybe retry works
                return func(WrappedChannel);
            }
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (!disposing ||
                Equals(_wrappedChannel, default(T)))
                return;
    
            var channel = _wrappedChannel as ICommunicationObject;
            _wrappedChannel = default(T);
            try
            {
                channel.Close();
            }
            catch (CommunicationException)
            {
                channel.Abort();
            }
            catch (TimeoutException)
            {
                channel.Abort();
            }
        }
    
        public void Dispose()
        {
            Dispose(true);
        }
    }
    

    Then you use the service like

    client.Invoke(channel => channel.DoStuff())