A Generic, IDisposable WCF Service Client
- by Steve Wilkes
WCF clients need to be cleaned up properly, but as they're usually auto-generated they don't implement IDisposable. I've been doing a fair bit of WCF work recently, so I wrote a generic WCF client wrapper which effectively gives me a disposable service client.
The ServiceClientWrapper is constructed using a WebServiceConfig instance, which contains a Binding, an EndPointAddress, and whether the client should ignore SSL certificate errors - pretty useful during testing! The Binding can be created based on configuration data or entirely programmatically - that's not the client's concern.
Here's the service client code:
using System;
using System.Net;
using System.Net.Security;
using System.ServiceModel;
public class ServiceClientWrapper<TService, TChannel> : IDisposable
where TService : ClientBase<TChannel>
where TChannel : class
{
private readonly WebServiceConfig _config;
private TService _serviceClient;
public ServiceClientWrapper(WebServiceConfig config)
{
this._config = config;
}
public TService CreateServiceClient()
{
this.DisposeExistingServiceClientIfRequired();
if (this._config.IgnoreSslErrors)
{
ServicePointManager.ServerCertificateValidationCallback =
(obj, certificate, chain, errors) => true;
}
else
{
ServicePointManager.ServerCertificateValidationCallback =
(obj, certificate, chain, errors) => errors == SslPolicyErrors.None;
}
this._serviceClient = (TService)Activator.CreateInstance(
typeof(TService),
this._config.Binding,
this._config.Endpoint);
if (this._config.ClientCertificate != null)
{
this._serviceClient.ClientCredentials.ClientCertificate.Certificate =
this._config.ClientCertificate;
}
return this._serviceClient;
}
public void Dispose()
{
this.DisposeExistingServiceClientIfRequired();
}
private void DisposeExistingServiceClientIfRequired()
{
if (this._serviceClient != null)
{
try
{
if (this._serviceClient.State == CommunicationState.Faulted)
{
this._serviceClient.Abort();
}
else
{
this._serviceClient.Close();
}
}
catch
{
this._serviceClient.Abort();
}
this._serviceClient = null;
}
}
}
A client for a particular service can then be created something like this:
public class ManagementServiceClientWrapper :
ServiceClientWrapper<ManagementServiceClient, IManagementService>
{
public ManagementServiceClientWrapper(WebServiceConfig config)
: base(config)
{
}
}
...where ManagementServiceClient is the auto-generated client class, and the IManagementService is the auto-generated WCF channel class - and used like this:
using(var serviceClientWrapper = new ManagementServiceClientWrapper(config))
{
serviceClientWrapper.CreateServiceClient().CallService();
}
The underlying WCF client created by the CreateServiceClient() will be disposed after the using, and hey presto - a disposable WCF service client.