DataServices, IErrorHandlers, Exceptions and the Gulf between them.

OK I am going to let you in on a dirty little secret about Astoria based services. This secret is probably not even known to you. Ready to hear it? Really? Well here it is:

 You'd better never have anything go wrong as you'll never know it. It devours exceptions and is hungry for more!

"What?!?!" you ask? That's right. It is the Roach Motel of exceptions. They check in but they don't check out.

You can add IErrorHandler instances in the service host behaviors collection all day long but they will never ever be called. PERIOD.

Why is this? While this is a gross over simplification but basically there's a giant try...catch statement in the DataService<T> class for the service opperation.
The way that data services handles exceptions breaks the WCF service behaviors model is this:

void IDataService.InternalHandleException(HandleExceptionArgs args)
{
    try
    {
        this.HandleException(args);
    }
    catch (Exception exception)
    {
        if (!WebUtil.IsCatchableExceptionType(exception))
        {
            throw;
        }
        args.Exception = exception;
    }
}

You see, in a nutshell the IErrorHandler concept in WCF works as thus:
If there's an unhandled exception anywhere inside the service class instance that trickles out, the WCF infrastructure will examine the set of IErrorHandler behaviors attached to the service context and if any, will supply the exception to them for action and then translation to a fault message. But, and here's the kicker, the DataService<T> type never allows any exceptions to trickle out so therefore prevents IErrorHandler implementations added to the channel dispatcher from being executed. This is BAD (we've actually opened an item on Connection regarding this but it was "closed, by design" asshattery).

This behavior does not appear to be documented anywhere and is highly unintuitive.

But there IS a workaround which I'll show you. It does require that you use a custom subclass of the DataService<T> type that you can create (you'll need a custom service host factory but that's a simple bit of work to create that I'll leave to you).
While we cannot override this method (though we could subclass and reimplement it but I don't suggest route), we can instead override the HandleException method as such:

protected override void HandleException( HandleExceptionArgs args )
{
    base.HandleException( args );
    this.ErrorHandlers.ForEach(handler => handler.HandleError(args.Exception));
}

All you need to do is when building up your custom instance, is to supply a sequence of IErrorHandlers in the constructor (this is where containers really shine).

So hopefully this will shed some light on the wierdness you might see or have seen in Astoria.

Comments

  • Anonymous
    November 25, 2013
    The comment has been removed
  • Anonymous
    November 25, 2013
    Never mind, I ended up throwing a DataServiceException and let me set the statusCode.