Castle Windsor 依赖项解析器适用于 MVC 3

34
自从IoC / DI在MVC 3中的实现很可能已经在RC版本中进入最终形式,我正在寻找使用Caste Windsor更新的DependencyResolver、IControllerActivator和IViewPageActivator的实现。是否有任何已经更新为MVC 3 RC的示例?EDIT#1:实现Windsor依赖项解析器确实是微不足道的,但仍然有一些遗漏。与Jeff Putz的Ninject示例(下面)相反,似乎这并不像Windsor那样简单。设置依赖项解析器后,就像这样:
DependencyResolver.SetResolver(new WindsorDependencyResolver(container)); 

Windsor抛出了ComponentNotFoundException异常。我需要提供IControllerFactory和IControllerActivator的实现。由于DefaultControllerFactory支持DependencyResolver,可以按以下方式解决此问题:

Component.For<IControllerFactory >().ImplementedBy<DefaultControllerFactory>()
Component.For<IControllerActivator >().ImplementedBy<WindsorControllerActivator>(),

WindsorControllerActivator非常简单。然而,这会导致另一个IViewPageActivator的ComponentNotFoundException。

这使我相信我漏掉了一些东西。这不可能比实现控制器工厂并以MVC 2.0方式调用ControllerBuilder.Current.SetControllerFactory更复杂。

编辑#2 我错过了一个微妙但非常重要的细节,即当找不到服务时,依赖项解析器需要返回null。实现如下:

public class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        return container.Kernel.HasComponent(serviceType) ? container.Resolve(serviceType) : null;
    }

  public IEnumerable<object> GetServices(Type serviceType)
    {
        return container.Kernel.HasComponent(serviceType) ? container.ResolveAll(serviceType).Cast<object>() : new object[]{};
    }
}

第三次编辑

回答评论区中的一个问题。如果你确实需要自己的IControllerActivator,这里提供了一个简单的Windsor实现:

public class WindsorControllerActivator : IControllerActivator
{
    private readonly IWindsorContainer container;

    public WindsorControllerActivator(IWindsorContainer container)
    {
        this.container = container;
    }

    public IController Create(RequestContext requestContext, Type controllerType)
    {
        return (IController)container.GetService(controllerType);
    }
}

}

再次强调,使用Windsor和MVC3的依赖解析器实现基本DI不需要此操作。

编辑 #4 根据一些进一步的研究和反馈,传统控制器工厂实现似乎是Windsor和MVC3的最佳方法。问题在于IDependencyResolver接口缺少一个release方法,这可能会导致Windsor不释放其组件而导致内存泄漏。如果所有的依赖都使用PerWebRequest 生命周期进行解析,则这可能不是问题,但最好不要冒险。这里是一个基本的Windsor控制器工厂实现示例用于MVC3。

public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IWindsorContainer container;

    public WindsorControllerFactory(IWindsorContainer container)
    {
        this.container = container;
    }

    public override void ReleaseController(IController controller)
    {
        container.Kernel.ReleaseComponent(controller);
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controllerComponentName = controllerName + "Controller";
        return container.Kernel.Resolve<IController>(controllerComponentName);
    }
}

编辑 #5 如果你正在使用MVC区域,上述实现将无法正常工作。你需要根据每个控制器的完整名称注册它们,并重写GetControllerInstance而不是CreateController:

 protected override IController GetControllerInstance(RequestContext context, Type controllerType)
    {
        if (controllerType != null)
        {
            return (IController)container.Kernel.Resolve(controllerType);
        }
        return null;
    }

5
MVC 3 已经发布 15 小时了,有人立即编写这段代码! - John Farrell
是的... null 返回部分很关键,如果您不知道 Ninject 具有 TryGet 方法并且无法找到匹配项,则在我的示例中可能不太明显。我可能可以更清楚一些。 :) - Jeff Putz
啊,谢谢。我是一个晚期加入Ioc教会的人,但现在我已经屈服于它并正在赶上。 - MvcCmsJon
这个激活器有缺陷,我试图使用它,但如果您将此激活器注册到您的 DI 容器中,则 DefaultControllerFactory 将使用此激活器。当调用 create 时,如果容器中没有控制器,则会失败,并给您与作者看到的相同错误消息。要解决这个问题,您可以从容器检查 null,然后在其为 null 时添加它。if (controller == null) { container.Register(Component .For(controllerType) .LifeStyle.Singleton); controller = (IController)container.GetService(controllerType); } - CrazyDart
基于这一切,我可能会使用Ninject。 - Jon
显示剩余2条评论
3个回答

24

10

自从beta版发布以来,该界面并未更改,因此各种框架的所有实现仍然可以使用。事实上,这不是一个很复杂的接口......你应该能够轻松地自己完成。例如,我为Ninject做了这个:

public class NinjectDependencyResolver : IDependencyResolver
{
    public NinjectDependencyResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    private readonly IKernel _kernel;

    public object GetService(Type serviceType)
    {
        return _kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _kernel.GetAll(serviceType);
    }
}

然后像这样在 global.asax 中把它连接起来:

    private static IKernel _kernel;
    public IKernel Kernel
    {
        get { return _kernel; }
    }

    public void Application_Start()
    {
        _kernel = new StandardKernel(new CoreInjectionModule());
        DependencyResolver.SetResolver(new NinjectDependencyResolver(Kernel));
        ...
    }

记住,在那一点上,您将获得各种免费好处,包括控制器的依赖项注入、控制器工厂、操作过滤器和视图基类。

编辑:为了明确起见,我不确定您的“激活器”是什么,但您可能不需要它们。IDependencyResolver接口会自动处理控制器和视图的新建。


嗨,为什么要将内核存储在应用程序类的公共静态属性中?我对Ninject还不熟悉,但我只需要这个吧:var resolver = new StandardKernel(new MyNinjectModule()); DependencyResolver.SetResolver(new NinjectDependencyResolver(resolver)); 谢谢! - Pete Montgomery

2

MVCContrib 目前是IoC-MVC集成的权威来源。目前,MVC3分支仅包括控制器工厂和IDependencyResolver实现(以及其他一些内容)。我建议fork该存储库并实现缺失的扩展点(不应太难),然后发送拉取请求给团队。


1
不再使用!对于现在阅读这篇文章的任何人:http://mvccontrib.codeplex.com/workitem/7122 - Chris Haines

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接