How to handle constructor exception when using Autofac WcfIntegration

Is there a way to handle the exception thrown by the WCF service constructor when this constructor accepts the dependency, and is it creating dependencies of the IoC container (in this case AutoFac) that throws 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 AutofacWebServiceHostFactory (this is usually a RESTful service).

Dependencies are recorded in the global.asax.cs service, that is:

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

The InformationRetriever implementation now performs some checks in its constructor to ensure that it can do its job. When it detects a problem in this phase, it throws an exception.

However, I do not want the calling service object to receive an AutoFac exception:

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

In fact, I'm trying to check:

This works InformationService

When I call the GetSomeInformation () method

And Unable to instantiate InformationRetriever

Then I want to return a welcome error message

And write down the actual exception

Is this a problem for my design, or is there a known pattern for overcoming or preventing this problem?

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

+6
source share
2 answers

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

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

... where FailoverInformationRetreiver throws exceptions when accessing a member. Another idea might be as follows:

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

and try/catch around use inside the InformationService . Another option you could go with if the availability of the InformationRetriever known at application 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; } 

Does any of these ideas help?

+2
source

Objects written in the DI style usually go through two separate phases: composition and execution. The composition phase is where you connect the dependencies and do things like throwing arguments away. Typically, you want this phase to not contain meaningful behavior, as this will cause errors in your system configuration. The second step, execution, is where you use the output of the first phase (dependencies) to do your work.

Separating these two phases eliminates a lot of ambiguity and complexity. For example, you are not trying to mow the lawn, and then carbonate the lawn mower; which causes both actions to become more complex (and dangerous!)

In this case, the InformationRetriever combines the layout and execution phases, performing meaningful work in its constructor. This confusion causes exactly the problem you are trying to avoid: a significant business exception is wrapped in a composite exception. It is also unclear how to handle the exception, because the top-level invoker is Autofac, not the component that actually asks InformationRetriever to do the work.

I suggest doing validation when calling InformationRetriever ; this eliminates the Autofac exception and allows the InformationService handle the exception without any deception.

One of the potential drawbacks of this approach is that the check will be performed with every call to InformationRetriever , and not in the constructor. You have two options: 1) Let it happen every time to be absolutely sure that the work is valid, or 2) Keep track of whether you have completed the check and do it only if you have not done it before.

If you select # 2, you can keep the InformationRetriever clean by using a decorator to wrap it in a validation 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 decorator support as follows:

 builder .RegisterType<InformationRetriever>() .Named<IInformationRetriever>("base"); builder.RegisterDecorator<IInformationRetriever>( (c, inner) => new ValidatingInformationRetriever(inner), fromKey: "base"); 
+8
source

Source: https://habr.com/ru/post/920491/


All Articles