Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor injection into a base class using autofac

I have an abstract base controller which has a constructor I hoped would be populated by autofac when the controllers were built.

public abstract class BaseController : Controller {     protected ILogger { get; private set; }      protected BaseController()     {     }      protected BaseController(ILogger logger)     {         Logger = logger;     } } 

This doesn't seem to work when I derive a controller from it.

I can only get this to work when I explicitly call the constructor explicitly from the controller. Is this the correct way to do this?

public class PublicController : BaseController {     public PublicController()     {     }      public PublicController(ILogger logger) : base(logger)     {      } } 

Also, using the MVC integration assembly, there doesn't seem to be a way to share the container for other classes to do their own resolving. I read somewhere that this is not encouraged, why not? Is this just to decouple the dependency of any single ioc framework? Is constructor injection the only way to populate dependencies down the hierarchy?

like image 568
gav Avatar asked Aug 26 '11 01:08

gav


People also ask

What is the use of Autofac in MVC?

AutoFac provides better integration for the ASP.NET MVC framework and is developed using Google code. AutoFac manages the dependencies of classes so that the application may be easy to change when it is scaled up in size and complexity.

What is @inject in constructor?

Constructor Injection is the most common form of Dependency Injection. Constructor Injection is the act of statically defining the list of required dependencies by specifying them as parameters to the class's constructor.

Does @inject call constructor?

@Inject can apply to at most one constructor per class. @Inject is optional for public, no-argument constructors when no other constructors are present. This enables injectors to invoke default constructors.


1 Answers

Calling the base class constructor explicitly is the only way to do this using constructor injection in C#. It looks like you should remove the parameterless constructors from BaseController and PublicController as they should never actually be called when a logger is available.

The problem of injecting dependencies into a base controller is a common one using ASP.NET MVC and IoC. There are several options/schools of thought.

1.) Use aggregate services. To keep derived class constructors simple, create a single service that exposes or delegates to all the different services needed by the base controller (e.g. IBaseControllerDependencies or similar.) Then pass this one service to the BaseController just as you are doing with ILogger here.

There are various pros/cons depending on your application and the number of base classes you're using. Google for 'Autofac aggregate services' to see more on this.

2.) Use property injection. Make the ILogger property on your base class public, and configure the container using:

builder.RegisterControllers().PropertiesAutowired(); 

Property injection isn't really a preferred technique in Autofac. The constructor's role is to accept dependencies, while writeable properties are often seen as a code smell, so Autofac doesn't really optimise for this case. One of the drawbacks is that writeable properties that shouldn't be injected often are mistakenly, with odd consequences.

3.) Refactor base controller functionality into various action filters. Autofac can inject action filters into the MVC action invocation pipeline. Thus the filters can take the dependencies that were on the base class, and the same concerns might be applied in a cross-cutting way. More info on this out on the web, ExtensibleActionInvoker and .InjectActionInvoker() point to the info you'd need. Not always possible with all concerns.

4, also the answer to your second question.) Resolve base controller dependencies using service location from DependencyResolver.Current.

var logger = DependencyResolver.Current.GetService<ILogger>(); 

The reason this isn't encouraged is that it makes the resulting application harder to understand because it is no longer possible to see what services a component depends upon by looking in one place (the constructor.) To determine what must be configured in the container before a particular component can be used, one has to look at the entire codebase of the component to find the GetService() calls. A noticeable impediment when unit testing.

Hope this helps, bit of a brain dump I know :) Others can probably add some more ideas to these.

like image 145
Nicholas Blumhardt Avatar answered Nov 15 '22 12:11

Nicholas Blumhardt