Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 607
David Avatar asked Mar 05 '13 12:03

David


2 Answers

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())
like image 127
adrianm Avatar answered Nov 13 '22 06:11

adrianm


Do something like that:

public interface IMyServiceClient : IMyService, ICommunicationObject { }

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

like image 2
TGlatzer Avatar answered Nov 13 '22 07:11

TGlatzer