Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle constructor exception when using Autofac WcfIntegration

Is there a way to handle an exception thrown by the constructor of a WCF service, when that constructor takes in a dependency, and it is the instantiation of the dependency by the IoC container (AutoFac in this case) that causes the exception?

Consider a WCF service with the following constructor:

public InformationService(IInformationRetriever informationRetriever)
{
    _informationRetriever = informationRetriever;
}
//... the service later makes use of the injected InformationRetriever

The service uses AutoFac WcfIntegration and the AutofacWebServiceHostFactory (this happens to be a RESTful service).

Dependencies are registered in the global.asax.cs of the service, i.e.:

builder.RegisterType<InformationRetriever>()
                .As<IInformationRetriever>()

Now the InformationRetriever implementation performs some checks in its constructor to ensure everything is in place for it to be able to do its job. When it discovers a problem in this phase, it throws an exception.

However, I do not want the caller of the service to receive the AutoFac exception:

An exception was thrown while invoking the constructor ... on type InformationRetriever

Effectively I am trying to test:

Given the InformationService is running

When I call the GetSomeInformation() method

And The InformationRetriever cannot be instantiated

Then I want to return a friendly error message

And Log the actual exception

Is this a problem with my design, or is there a known pattern to overcome or prevent this problem?

I have hunted around and could not find any information on this type of problem.

like image 828
David Sette Avatar asked Jul 14 '12 16:07

David Sette


2 Answers

Objects written in the DI style generally pass through two separate phases: composition and execution. The composition phase is where you wire up dependencies and do things like throw argument exceptions. You generally want to keep this phase free of meaningful behavior, as that allows you to surface errors in the configuration of your system. The second phase, execution, is where you use the output of the first phase (dependencies) to do your work.

Separating these two phases removes a lot of ambiguity and complexity. As an example, you don't try to mow your lawn while gassing up your lawnmower; that causes both activities to become more complex (and dangerous!)

In this case, InformationRetriever is conflating the composition and execution phases by performing meaningful work in its constructor. This mixing is causing exactly the issue you are trying to avoid: a meaningful business exception being wrapped in a composition exception. It is also unclear how to handle the exception, since the top-level invoker is Autofac and not the component which is actually asking InformationRetriever to do work.

I suggest striving to do the validation when calling on InformationRetriever; this removes the Autofac exception and allows InformationService to handle the exceptional situation without any trickery.

One potential downside of this approach is that the validation will happen on every call to InformationRetriever, rather than once in the constructor. You have two choices: 1) Let it happen every time, to be absolutely sure the work is valid to do, or 2) Keep track of whether you've done the check and only do it if you haven't before.

If you choose #2, you can keep InformationRetriever clean by using a decorator to wrap it in a validating version of the same interface:

public class ValidatingInformationRetriever : IInformationRetriever
{
    private readonly IInformationRetriever _baseRetriever;
    private bool _validated;

    public ValidatingInformationRetriever(IInformationRetriever baseRetriever)
    {
        _baseRetriever = baseRetriever;
    }

    public void Foo()
    {
        if(!_validated)
        {
            Validate();

            _validated = true;
        }

        _baseRetriever.Foo();
    }

    private void Validate()
    {
        // ...
    }
}

You can register it using Autofac's decorator support like so:

builder
    .RegisterType<InformationRetriever>()
    .Named<IInformationRetriever>("base");

builder.RegisterDecorator<IInformationRetriever>(
    (c, inner) => new ValidatingInformationRetriever(inner),
    fromKey: "base");
like image 133
Bryan Watts Avatar answered Oct 23 '22 19:10

Bryan Watts


I'm not a big fan of constructors throwing exceptions for reasons other than bad arguments. I'd probably model my types a different way. But here's some ideas. At first I thought about doing something like this:

builder
    .Register(c => {
        try
        {
            return new InformationRetriever();
        }
        catch (Exception)
        {
            return new FailoverInformationRetreiver();
        }})
    .As<IInformationRetriever>();

... where FailoverInformationRetreiver throws exceptions on member access. Another idea might be to do:

public InformationService(Lazy<IInformationRetriever> informationRetriever)
{
    _informationRetriever = informationRetriever;
}

and try/catch around usages inside InformationService. Another option you could go with if the availability of InformationRetriever is known at app startup:

// inside your container builder:
if (InformationRetreiver.IsAvailable())
    builder.RegisterType<InformationRetriever>()
           .As<IInformationRetriever>()

// inside InformationService, make the dependency optional
public InformationService(IInformationRetriever informationRetriever = null)
{
    _informationRetriever = informationRetriever;
}

Do any of those ideas help?

like image 39
Jim Bolla Avatar answered Oct 23 '22 18:10

Jim Bolla