Question

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?

Was it helpful?

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())

OTHER TIPS

Do something like that:

public interface IMyServiceClient : IMyService, ICommunicationObject { }

If you create a channel for that, you can dispose it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top