Problem
I like to use using blocks to instantiate my WCF service clients because it’s pretty much the usual way to access resources that implement IDisposable:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
However, as this MSDN article points out, putting a WCF client in a using block can hide any faults that cause the client to get faulted (like a timeout or communication problem). To summarize, when Dispose() is called, the client’s Close() method is called, but it throws an error because the client is in a faulted condition. The first exception is therefore obscured by the second one. This isn’t good.
According to the MSDN article, the best workaround is to completely avoid using a using block and instead instantiate your clients and utilize them like this:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
That, in comparison to the utilizing block, I believe, is unsightly. Also, every time you require a client, you’ll have to create a lot of code.
Fortunately, I discovered a couple of additional solutions, including this one on the (now defunct) IServiceOriented blog. You begin with:
public delegate void UseServiceDelegate<T>(T proxy);
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
Which then allows:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
That’s not bad, but I don’t think it’s as expressive and easily understandable as the using block.
I initially learned about the workaround I’m currently attempting on blog.davidbarret.net. Basically, wherever you utilise the client’s Dispose() method, you override it. Something like:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
This looks to allow the using block to be used once more without the risk of disguising a faulty state exception.
So, are there any other gotchas I have to look out for using these workarounds? Has anyone come up with a better idea?
Asked by Eric King
Solution #1
Despite the fact that I posted about it (see Luke’s response), I believe this is superior to my IDisposable wrapper. Code example:
Service<IOrderService>.Use(orderService=>
{
orderService.PlaceOrder(request);
});
(edit per comments)
Because Use returns void, the most straightforward approach to deal with return values is to use a captured variable:
int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
{
newOrderId = orderService.PlaceOrder(request);
});
Console.WriteLine(newOrderId); // should be updated
Answered by Marc Gravell
Solution #2
If I had to choose between IServiceOriented.com’s solution and David Barret’s blog’s solution, I would choose the simplicity of overriding the client’s Dispose() method. This allows me to continue to utilize the using() statement with a disposable object, as one would anticipate. However, as @Brian pointed out, this solution has a race problem in that the State may not be faulted when it is checked, but may be by the time Close() is invoked, resulting in a CommunicationException.
To get around this, I devised a strategy that combines the best of both worlds.
void IDisposable.Dispose()
{
bool success = false;
try
{
if (State != CommunicationState.Faulted)
{
Close();
success = true;
}
}
finally
{
if (!success)
Abort();
}
}
Answered by Matt Davis
Solution #3
To make it operate properly, I developed a higher level function. This has been used in a number of projects and appears to operate well. Without the “using” concept or anything like that, this is how things should have been done from the start.
TReturn UseService<TChannel, TReturn>(Func<TChannel, TReturn> code)
{
var chanFactory = GetCachedFactory<TChannel>();
TChannel channel = chanFactory.CreateChannel();
bool error = true;
try {
TReturn result = code(channel);
((IClientChannel)channel).Close();
error = false;
return result;
}
finally {
if (error) {
((IClientChannel)channel).Abort();
}
}
}
You can make calls in the following manner:
int a = 1;
int b = 2;
int sum = UseService((ICalculator calc) => calc.Add(a, b));
Console.WriteLine(sum);
This is very similar to what you have in your example. We develop highly typed helper methods in some applications, thus we wind up with something like “Wcf.UseFooService(f=>f…)”.
All things considered, I find it rather elegant. Is there a specific issue you’ve run into?
This enables the addition of other useful features. For example, on one site, the site authenticates the logged in user on behalf of the service. (The site does not have any credentials on its own.) We can customize the channel factory anyway we want by writing our own “UseService” method helper, for example. We’re also not obligated to use the proxies that are generated; any interface will suffice.
Answered by MichaelGG
Solution #4
Microsoft’s suggested method for handling WCF client calls is as follows:
Expected Exceptions has further information.
try
{
...
double result = client.Add(value1, value2);
...
client.Close();
}
catch (TimeoutException exception)
{
Console.WriteLine("Got {0}", exception.GetType());
client.Abort();
}
catch (CommunicationException exception)
{
Console.WriteLine("Got {0}", exception.GetType());
client.Abort();
}
supplementary information This concern appears to be so common on WCF that Microsoft built a special sample to show how to handle exceptions:
c:\WF_WCF_Samples\WCF\Basic\Client\ExpectedExceptions\CS\client
C# or VB.NET samples are available for download.
Given the plethora of difficulties surrounding the using statement, (heated?) I’m not going to waste my time attempting to become a code cowboy and find a cleaner way, despite internal conversations and threads on the subject. For my server apps, I’ll just put up with it and create WCF clients in this verbose (but trusted) manner.
Additional Failures to Catch (Optional)
CommunicationException is the root of a lot of exceptions, and I don’t think most of them should be retried. I went over each exception on MSDN and came up with a short list of exceptions that may be retried (in addition to TimeOutException above). Please let me know if there is an exception that needs to be retried.
// The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
catch (ChannelTerminatedException cte)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}
// The following is thrown when a remote endpoint could not be found or reached. The endpoint may not be found or
// reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
catch (EndpointNotFoundException enfe)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}
// The following exception that is thrown when a server is too busy to accept a message.
catch (ServerTooBusyException stbe)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}
To be honest, writing this code is a bit of a chore. I currently prefer this answer, and don’t see any “hacks” in that code that may cause issues down the road.
Answered by makerofthings7
Solution #5
lly discovered some strong steps toward a complete answer to this issue.
Exception Handling WCF Proxy Generator is a project on Codeplex. It essentially adds a new custom tool to Visual Studio 2008, which is then used to construct the new service proxy (Add service reference). It has some nice functionality to deal with faulted channels, timeouts and safe disposal. There’s an excellent video here called ExceptionHandlingProxyWrapper explaining exactly how this works.
You can safely use the Using statement again, and the Wrapper will re-initialize the faulted channel and repeat the query if the channel is faulted on any request (TimeoutException or CommunicationException). If that fails, it will use the Abort() command to terminate the process, dispose of the proxy, and rethrow the Exception. If the service throws a FaultException code, it will stop running and the proxy will be aborted properly, as planned, throwing the correct error.
Answered by Neil
Post is based on https://stackoverflow.com/questions/573872/what-is-the-best-workaround-for-the-wcf-client-using-block-issue