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?
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())
Do something like that:
public interface IMyServiceClient : IMyService, ICommunicationObject { }
If you create a channel for that, you can dispose it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With