I'm having trouble with Autofac constructor injection. I'm somewhat solved my problem but I'm turning to the community for a full solution.
This works with DI
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerHttpRequest();
This does not.
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.AsImplementedInterfaces()
.InstancePerHttpRequest();
Here's my controller.
public class HomeController : BaseController
{
public HomeController(IPlugin plugin)
{
}
}
I have a module to resolve the IPlugin injection. I'm wondering why I need to take the .AsImplementedInterfaces()
out to make this work. I would prefer to use interfaces because I'm using MEF to import IControllers from other assemblies at runtime.
Update
Thanks to japonex (see comments below) I've updated my solution. Here's what I did to get everything working.
First my project has its own controllers, and I then use MEF to import controllers from other assemblies.
Controllers for my current project must be registered without the .AsImplementedInterfaces()
call, so like so
builder.RegisterControllers(typeof(MvcApplication).Assembly).InstancePerRequest();
This will put the controllers into Autofac using the type instead of as an interface (MyProject.Controllers.HomeController
instead of System.Web.Mvc.IController
).
Next in my plugin project I simply had to export the types instead of as an interface. So I use this
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : PluginController
Instead of this
[Export(typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : PluginController
Notice the difference in the Export
attribute.
Next I set the current DependencyResolver
to the AutofacDependencyResolver
.
Last, but not least, I created a custom ControllerFactory to create the controllers. Technically this isn't needed, since I'm no longer using interfaces, but I have code to check the status of the controller before creating it. This allows me to easily enable/disable a plugin from an admin area.
So the root of the problem is that Autofac needed types instead of interfaces to resolve properly. I'm sure this could be done with interfaces, but this solution works for me. The only real reason I would have wanted to use interfaces is because I could then ask for all controllers from Autofac without knowing their type.
public class MyControllerFactory : DefaultControllerFactory
{
private readonly IContainer _container;
public PortalControllerFactory(IContainer container)
{
_container = container;
}
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
Type controllerType = GetControllerType(requestContext, controllerName);
if(controllerType == null)
{
throw new HttpException(404, ErrorMessages.Http404);
}
IPlugin plugin = PluginManager.Current.GetPlugin(controllerType);
if (plugin != null && plugin.Status != Common.Enums.PluginStatus.Enabled)
{
//the controller/plugin is disabled so modify the route data and return the controller
RouteData data = new RouteData();
data.Values.Add("controller", "plugin");
data.Values.Add("action", "disabled");
data.Values.Add("plugin", plugin.Name);
requestContext.RouteData = data;
return ViewRenderer.CreateController<Project.Controllers.PluginController>(data);
}
var controller = ((AutofacDependencyResolver)DependencyResolver.Current).RequestLifetimeScope.Resolve(controllerType);
return controller as IController;
}
}
When you use .AsImplementedInterfaces
you're saying that for each concrete type, Autofac registers this type for each interface implementation.
So, you're registering all controllers in your Assembly to IController and propably because you don't have any rule (keyed or named registration) to decide which concrete type return, Autofac probably returns the last registration for IController.
Try call container.Resolve and see what you get.
Consider that if you have:
public class HomeController : BaseController
{
public HomeController(IPlugin plugin)
{
}
}
and
public class Home2Controller : BaseController
{
public Home2Controller(IPlugin plugin)
{
}
}
HomeController will be registered to IController and the Home2Controller also will be registered to IController.
And I think the default behavior of Autofac is to use the last registration if you don't use any kind of rule (keyed or named registration)
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