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.
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");
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With