I like instantiating my WCF service clients within a using
block as it\'s pretty much the standard way to use resources that implement IDisposable
:
The following helper allows to call void
and non-void methods. Usage:
var calculator = new WcfInvoker<CalculatorClient>(() => new CalculatorClient());
var sum = calculator.Invoke(c => c.Sum(42, 42));
calculator.Invoke(c => c.RebootComputer());
The class itself is:
public class WcfInvoker<TService>
where TService : ICommunicationObject
{
readonly Func<TService> _clientFactory;
public WcfInvoker(Func<TService> clientFactory)
{
_clientFactory = clientFactory;
}
public T Invoke<T>(Func<TService, T> action)
{
var client = _clientFactory();
try
{
var result = action(client);
client.Close();
return result;
}
catch
{
client.Abort();
throw;
}
}
public void Invoke(Action<TService> action)
{
Invoke<object>(client =>
{
action(client);
return null;
});
}
}
Override the client's Dispose() without the need to generate a proxy class based on ClientBase, also without the need to manage channel creation and caching! (Note that WcfClient is not an ABSTRACT class and is based on ClientBase)
// No need for a generated proxy class
//using (WcfClient<IOrderService> orderService = new WcfClient<IOrderService>())
//{
// results = orderService.GetProxy().PlaceOrder(input);
//}
public class WcfClient<TService> : ClientBase<TService>, IDisposable
where TService : class
{
public WcfClient()
{
}
public WcfClient(string endpointConfigurationName) :
base(endpointConfigurationName)
{
}
public WcfClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public WcfClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress)
{
}
public WcfClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress)
{
}
protected virtual void OnDispose()
{
bool success = false;
if ((base.Channel as IClientChannel) != null)
{
try
{
if ((base.Channel as IClientChannel).State != CommunicationState.Faulted)
{
(base.Channel as IClientChannel).Close();
success = true;
}
}
finally
{
if (!success)
{
(base.Channel as IClientChannel).Abort();
}
}
}
}
public TService GetProxy()
{
return this.Channel as TService;
}
public void Dispose()
{
OnDispose();
}
}
public static class Service<TChannel>
{
public static ChannelFactory<TChannel> ChannelFactory = new ChannelFactory<TChannel>("*");
public static TReturn Use<TReturn>(Func<TChannel,TReturn> codeBlock)
{
var proxy = (IClientChannel)ChannelFactory.CreateChannel();
var success = false;
try
{
var result = codeBlock((TChannel)proxy);
proxy.Close();
success = true;
return result;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
So it allows to write return statements nicely:
return Service<IOrderService>.Use(orderService =>
{
return orderService.PlaceOrder(request);
});
I like this way of closing connection:
var client = new ProxyClient();
try
{
...
client.Close();
}
finally
{
if(client.State != CommunicationState.Closed)
client.Abort();
}
I used Castle dynamic proxy to solve the Dispose() issue, and also implemented auto-refreshing the channel when it is in an unusable state. To use this you must create a new interface that inherits your service contract and IDisposable. The dynamic proxy implements this interface and wraps a WCF channel:
Func<object> createChannel = () =>
ChannelFactory<IHelloWorldService>
.CreateChannel(new NetTcpBinding(), new EndpointAddress(uri));
var factory = new WcfProxyFactory();
var proxy = factory.Create<IDisposableHelloWorldService>(createChannel);
proxy.HelloWorld();
I like this since you can inject WCF services without consumers needing to worry about any details of WCF. And there's no added cruft like the other solutions.
Have a look at the code, it's actually pretty simple: WCF Dynamic Proxy
Use an extension method:
public static class CommunicationObjectExtensions
{
public static TResult MakeSafeServiceCall<TResult, TService>(this TService client, Func<TService, TResult> method) where TService : ICommunicationObject
{
TResult result;
try
{
result = method(client);
}
finally
{
try
{
client.Close();
}
catch (CommunicationException)
{
client.Abort(); // Don't care about these exceptions. The call has completed anyway.
}
catch (TimeoutException)
{
client.Abort(); // Don't care about these exceptions. The call has completed anyway.
}
catch (Exception)
{
client.Abort();
throw;
}
}
return result;
}
}