Q:

How to handle a hierarchy of exceptions - by wrapping or by inheritance?

I'm writing a reusable library, and I'm looking for a way to handle the exceptions that might occur during the processing. For example, I have the following class:
public interface IObjectFetcher
{
    public IObject GetObject(int objectId);
}

public class WidgetFetcher : IObjectFetcher
{
    public IObject GetObject(int objectId)
    {
        var fileName = DetermineWidgetFileName(objectId);
        var widget = GetWidgetFromFile(fileName);
        widget.Frob = GetWidgetFrob(objectId);
        return widget;
    }

    private string DetermineWidgetFileName(int widgetId)
    {
        return someExternalDbService.GetFileNameForWidget(widgetId); //throws DbException
    }

    private Widget GetWidgetFromFile(string fileName)
    {
        return someExternalFileService.GetWidgetFromFile(fileName); //throws FileException
    }

    private Frob GetFrobForWidget(int widgetId)
    {
        return someExternalDbService.GetFrobForWidget(widgetId); //throws DbException
    }
}

How to handle the DbException and FileException so that:

the exception contract of IObjectFetcher.GetObject doesn't get too complex and can allow different implementations
there's a way to access the underlying cause of the exception, like a failed database connection or a missing file
there's a way to differentiate between the DbException caused by looking up a file name and the DbException caused by retrieving a Frob
it's possible to attach the exception metadata like the object ID and the file name in a structured way, eg. to extract exception message templates into a resource file

It seems to me that I should create custom exceptions for each step of the process (with the InnerException being either the DbException or the FileException), and then I have two ways of approaching the problem - either have those custom exceptions inherit from a single base class, or have them wrapped by a single exception class. Which approach is correct? Or should I simplify it further?

A:

IMHO a complex exception class structure is often just over-engineering.
First make sure that the bullets you listed are really requirements of some user of your library.
I'd especially question the importance of differentiating between different exception causes. Who needs that? from my experience, an exception is meant to:

Tell the method's caller that it failed to fulfill its contract. This doesn't need multiple exception types.
Support the logging of problems, by providing a useful message and a stack trace. This also doesn't need different exception classes.
Allow the caller to retry the action that failed, maybe deciding on the best alternative based on the type of failure. Doing that can make good use of the exception type, but I've rarely seen such a pattern in real software.

So if you think some user of your library will want do do "intelligent retries" (and your library provides a chance to do it), go for an exceptions class hierarchy that allows for that, otherwise only make sure that the exceptions carry decent messages for logging purposes.

