使用Autofac和Mvc控制器构造函数注入

时间:2014-07-18 21:09:10

标签: c# asp.net-mvc mef autofac

我遇到了Autofac构造函数注入问题。我有点解决了我的问题,但我转向社区寻求完整的解决方案。

这适用于DI

builder.RegisterControllers(typeof(MvcApplication).Assembly)
       .InstancePerHttpRequest();

这不是。

builder.RegisterControllers(typeof(MvcApplication).Assembly)
       .AsImplementedInterfaces()
       .InstancePerHttpRequest();

这是我的控制器。

public class HomeController : BaseController
{
    public HomeController(IPlugin plugin)
    {

    }
}

我有一个模块来解析IPlugin注入。我想知道为什么我需要取.AsImplementedInterfaces()来完成这项工作。我更喜欢使用接口,因为我在运行时使用MEF从其他程序集导入IControllers。

更新

感谢japonex(见下面的评论)我已经更新了我的解决方案。以下是我为使一切工作所做的一切。

首先我的项目有自己的控制器,然后我使用MEF从其他程序集导入控制器。

我当前项目的控制器必须在没有.AsImplementedInterfaces()调用的情况下注册,所以

builder.RegisterControllers(typeof(MvcApplication).Assembly).InstancePerRequest();

这将使用类型而不是作为接口(MyProject.Controllers.HomeController而不是System.Web.Mvc.IController)将控制器置于Autofac中。

接下来在我的插件项目中,我只需要导出类型而不是作为接口。 所以我用这个

[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : PluginController

而不是这个

[Export(typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : PluginController

请注意Export属性的差异。

接下来,我将当前DependencyResolver设置为AutofacDependencyResolver

最后,但并非最不重要的是,我创建了一个自定义ControllerFactory来创建控制器。从技术上讲,这不是必需的,因为我不再使用接口,但我有代码在创建之前检查控制器的状态。这允许我从管理区域轻松启用/禁用插件。

问题的根源在于Autofac需要类型而不是接口才能正确解析。我确信这可以用接口完成,但这个解决方案对我有用。我想要使​​用接口的唯一真正原因是因为我可以在不知道其类型的情况下从Autofac请求所有控制器。

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;
    }
}

1 个答案:

答案 0 :(得分:1)

当您使用.AsImplementedInterfaces时,您会说每个具体类型,Autofac会为每个接口实现注册此类型。

因此,您正在将程序集中的所有控制器注册到IController,并且可能因为您没有任何规则(键控或命名注册)来决定返回哪种具体类型,Autofac可能会返回IController的最后一次注册。

尝试调用容器。解析,看看你得到了什么。

考虑一下,如果你有:

 public class HomeController : BaseController
{
    public HomeController(IPlugin plugin)
    {

    }
}

  public class Home2Controller : BaseController
{
    public Home2Controller(IPlugin plugin)
    {

    }
}

HomeController将注册到IController和 Home2Controller也将注册到IController。

我认为,如果您不使用任何类型的规则(键控或命名注册),Autofac的默认行为是使用最后一次注册