I created a WCF singleton service with netNamedPipeBinding. When a channel exception occurs, it leaves a channel in a faulty state, and all subsequent operations throws exceptions. How can I prevent this? I want that a TimeoutException, or any of the other common exceptions, to make only one operation fail, and not to make the service unresponsive.
You need to wrap your exceptions on the server side into FaultException. If you control both ends of the wire, and both ends are .NET, you can just simply wrap a .NET exception into a FaultException<T>
and send that back to the client. That way the channel stays usable.
See Specifying and Handling Faults in Contracts and Services on MSDN for more info, plus check out this article on CodeProject and this blog post Why Use WCF FaultException on the same topic.
Also, to make your life easier, check out the IErrorHandler interface which you can implement on the server side to globally catch exceptions and convert them into faults. Also see lots of blog posts on the topic, i.e. IErrorHandler: a generic Fault Converter or many other (Bing or Google for more).
To make your life even easier, you can implement the IErrorHandler as a behavior, which you can turn on or off on your service - either in service config, or by setting an attribute on your service class. There's quite a few implementation floating around the internet, one I like is here: Useful WCF Behavior: IErrorHandler.
There is another way. You can instantiate the client proxy for each request instead of using a single instance for more than one request. This way if the channel enters a faulty state, it is discarded anyway.
This is a bit tricky because you shouldn't dispose a channel, besides it being IDisposable.
It won't work:
using(var channel = channelFactory.CreateChannel())
{
return channel.ServiceMethod(parameter);
}
Instead you should:
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static TResult Use<TResult>(Func<T, TResult> func)
{
TResult output;
var channel = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
output = func((T)proxy);
channel.Close();
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
return output;
}
}
return Service<IService>.Use(channel =>
{
return channel.ServiceMethod(parameter);
});