I can’t take credit for this one, but I just had to pass it on. Visual Stuart showed me this neat little trick for safely working with WCF proxies. The problem with WCF is that if the server throws an exception, it faults the channel. This means that basically, you need to shut down the channel before trying to use it again or you will be in a nasty state indeed. Here is a handy little extension method to close down your WCF channels nicely.
public static void SafeClose(this ICommunicationObject proxy){try
{proxy.Close();}catch
{proxy.Abort();}}
So now you can easily wrap it in a generic to be used with all your proxies, like so:
Kudos to James for this one.
public class SafeProxy<Service> : IDisposable where Service : ICommunicationObject{private readonly Service _proxy;public SafeProxy(Service s)
{_proxy = s;}public Service Proxy
{get { return _proxy; }}public void Dispose(){if (_proxy != null)_proxy.SafeClose();}}Just use it like this without having to litter your code with try / catch.
using (var proxyVar = new SafeProxy<Proxy>(new Proxy())){proxyVar.MakeAnOperationCall();}Now, that’s just nifty.
I think this code may have issues as described here: https://elegantcode.com/2009/07/13/handy-wcf-techniques/
Here is what I have been using. Note that it has 2 Execute methods because 1 returns void. Using it is as easy as this: _proxy.Execute(s => s.GetAllSettingsLicenses(productId));
Also, this allows a single place for turning known service faults back into standard .NET exceptions and supports Basic Authentication. I use an StructureMap to construct these and inject the connection string.
public class ChannelManager
{
#region Delegates
public delegate TResult ExecuteDelegate(T proxy);
public delegate void ExecuteVoidDelegate(T proxy);
#endregion
private static Dictionary _connectionParameters;
private string _baseUri;
private string _endpointName;
private ChannelFactory _factory;
private string _password;
private string _username;
public ChannelManager(string connectionString)
{
Initialize(connectionString);
}
private void Initialize(string connectionString)
{
Dictionary connectionParameters =
ConfigurationHelpers.GetConnectionParameters(connectionString);
_endpointName =
ConfigurationHelpers.GetConnectionParameter(connectionParameters, connectionString, “bindingPrefix”)
typeof(T).Name;
_baseUri = ConfigurationHelpers.GetConnectionParameter(connectionParameters, connectionString, “baseUri”);
_username = ConfigurationHelpers.GetConnectionParameter(connectionParameters, connectionString, “username”);
_password = ConfigurationHelpers.GetConnectionParameter(connectionParameters, connectionString, “password”);
CreateFactory();
}
private void CreateFactory()
{
var endpointAddress = _baseUri ConfigurationHelpers.GetServiceClientEndpointAddress(_endpointName);
_factory = new ChannelFactory(_endpointName, new EndpointAddress(endpointAddress));
#if DEBUG
ServicePointManager.ServerCertificateValidationCallback = delegate { return (true); };
#endif
_factory.Faulted = ChannelFactory_Faulted;
if (!string.IsNullOrEmpty(_username))
{
_factory.Credentials.UserName.UserName = _username;
_factory.Credentials.UserName.Password = _password;
}
_factory.Open();
}
public void SetCredentials(string userName, string password)
{
_username = userName;
_password = password;
_factory.Abort();
}
public T CreateChannel()
{
if (_factory.State == CommunicationState.Closed)
{
CreateFactory();
}
T channel = _factory.CreateChannel();
Communication(channel).Faulted = ChannelFactory_Faulted;
return channel;
}
// http://www.iserviceoriented.com/blog/post/Indisposable – WCF Gotcha 1.aspx
public TResult Execute(ExecuteDelegate codeBlock)
{
TResult returnValue = default(TResult);
T proxy = CreateChannel();
try
{
returnValue = codeBlock(proxy);
((IClientChannel)proxy).Close();
}
catch (EndpointNotFoundException ex)
{
// TODO: Where could I move this to so that we find bad endpoints earlier
throw ex;
}
catch (FaultException ex)
{
throw new ProviderException(ex.Message, ex.InnerException);
}
catch (FaultException ex)
{
throw new ValidationException(ex.Detail.ValidationErrors);
}
catch (FaultException ex)
{
throw new SecurityException(ex.Message, ex.InnerException);
}
catch (FaultException ex)
{
throw new FileNotFoundException(ex.Message, ex.InnerException);
}
catch (Exception ex)
{
if (proxy != null)
{
((IClientChannel)proxy).Abort();
}
// TODO: Remove this
throw;
}
return returnValue;
}
public void Execute(ExecuteVoidDelegate codeBlock)
{
T proxy = CreateChannel();
try
{
codeBlock(proxy);
((IClientChannel)proxy).Close();
}
catch (EndpointNotFoundException ex)
{
// TODO: Where could I move this to so that we find bad endpoints earlier
throw ex;
}
catch (FaultException ex)
{
throw new ProviderException(ex.Message, ex.InnerException);
}
catch (FaultException ex)
{
throw new ValidationException(ex.Detail.ValidationErrors);
}
catch (FaultException ex)
{
throw new SecurityException(ex.Message, ex.InnerException);
}
catch (FaultException ex)
{
throw new FileNotFoundException(ex.Message, ex.InnerException);
}
catch (Exception ex)
{
if (proxy != null)
{
((IClientChannel)proxy).Abort();
}
}
}
public ICommunicationObject Communication(T channel)
{
return (ICommunicationObject)channel;
}
private void ChannelFactory_Faulted(object sender, EventArgs e)
{
ResetProxy();
}
private void ResetProxy()
{
_factory.Abort();
CreateFactory();
}
}
Hi. Maybe, you mean:
using (var proxyVar = new SafeProxy(new Proxy()))
{
proxyVar.Proxy.MakeAnOperationCall();
}