The guidance from this post has been added to the WCF Data Services Service Operations topic


In responding to a customer post on the WCF Data Services forum, I ran across an interesting behavior when raising exceptions in a service operation. When creating a data service, you are supposed to use the DataServiceException class when raising exceptions. This is because the data service knows how to map properties of this exception object to correctly to the HTTP response message. There is even a HandleException method on DataService<T> that you can override to return other kinds of exceptions as DataServiceException. As such, I was a little surprised to discover that errors raised in a service operation were being returned not as DataServiceException but instead wrapped with a TargetInvocationException. This may not seem like a big deal, but when UseVerboseErrors is set to false (the default), only information on the topmost exception is returned. This means that not only is the HTTP response code lost (it defaults to 500—the general “internal server error” message) but the nice custom error string is also buried.

The way to deal with this is to override the HandleException method and unpack the DataServiceException from the TargetInvocationException. Since the HandleException method is invoked when any exception is raised, we need to be sure to only handle the right kinds of errors (although it’s also a good practice to handle other non-data service exceptions in this method too). The following is an example implementation of HandleException that unpacks the DataServiceException and returns it as the top-level error:

// Override to manage returned exceptions.
protected override void HandleException(HandleExceptionArgs args)
{
    // Handle exceptions raised in service operations.
    if (args.Exception isTargetInvocationException 
        && args.Exception.InnerException != null)
    {
        if (args.Exception.InnerException.GetType()
            == typeof(DataServiceException))
        {
            // Unpack the DataServiceException.
            args.Exception = args.Exception.InnerException;
        }
        else
        {
            // Return a new DataServiceException as "400: bad request."
            args.Exception =
                new DataServiceException(400,
                    args.Exception.InnerException.Message);
        }
    }
}

[WebGet]
public void RaiseError()
{
    throw new DataServiceException(500,
        "My custom error message.");
}

(Note that the RaiseError service operation simply raises an error to test the implementation.) This implementation also handles other kinds of errors raised in service operations.

You may also be interested in the fact that one of the nifty things about HandleExceptionArgs is that it has a UseVerboseErrors property, which enables you to return inner exception information at runtime even when verbose errors are turned off for the data service (when it is safe to do so).

Cheers!

Glenn Gailey