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 BaseContr
I don't have comment privileges to ask for more detail, but I'd need to see your registration code to understand what you expect and why the container isn't meeting those expectations.
As to your additional questions: there isn't necessarily a way for a class to do it's own resolving, but a class can take a dependency on an automatically resolvable object factory for a known type (which most often suffices). If you have registered A
, you can take a dependency on Func<A>
. There are many relationship types like this in Autofac; see http://code.google.com/p/autofac/wiki/RelationshipTypes.
"Sharing the container" is discouraged because it tends to hide a class's dependencies. If a dependency on the container is taken, or referenced as a global "IoC" object, the expressiveness of specific dependencies is lost.
Resolving dependencies down the hierarchy shouldn't be a problem, since you're already using c'tor injection I'm not sure what your issue is there. There are ancillary concerns, such as modifying all your subclasses if your base class dependencies change. Fortunately, Autofac has an answer for that in Aggregate Services (http://code.google.com/p/autofac/wiki/AggregateService).
Autofac is a great, deep tool. One more suggestion, if you are just starting out please take some time to wrap your head around object lifetimes and lifetime scopes (especially http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/). Good luck!
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.
Autofac for .NET Core
Install
PM> Install-Package Autofac.Core.NonPublicProperty
Use
builder.RegisterType<AppService>()
.AsImplementedInterfaces()
.AutoWireNonPublicProperties();