Dispose Channel created by WCF ChannelFactory
Asked Answered
C

2

10

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?

Christlike answered 5/3, 2013 at 12:3 Comment(3)
Don't dispose the channel like that. Use: try { channel.Close(); } catch (CommunicationException) { channel.Abort(); } catch (TimeoutException) { channel.Abort(); }Caboodle
How come? Doesn't disposing the channel do the same for me?Christlike
Dispose will call Close. Close might throw. Write all your using statements as: try { using (...) { ... } } catch (CommunicationException) { } or use my pattern above.Caboodle
C
8

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())
Caboodle answered 5/3, 2013 at 12:58 Comment(2)
I think I'll keep the factory as it is, with the exception of your suggestion in the comments.Christlike
@espo, thank your for the code cleanup. I removed the .? which I assume was a misstake.Caboodle
S
2

Do something like that:

public interface IMyServiceClient : IMyService, ICommunicationObject { }

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

Smalley answered 5/3, 2013 at 12:10 Comment(2)
That would require a new interface per service though. I'd like to have a generic reusable approach.Christlike
Well, it's just an descriptive interface - there's no further implementation needed. You won't get it shorter, i thinkSmalley

© 2022 - 2024 — McMap. All rights reserved.