【问题标题】:Setting up Inversion of Control (IoC) in ASP.NET MVC with Castle Windsor使用 Castle Windsor 在 ASP.NET MVC 中设置控制反转 (IoC)
【发布时间】:2011-02-26 15:57:30
【问题描述】:

我正在阅读 Sanderson 的 Pro ASP.NET MVC 框架,在第 4 章中他讨论了Creating a Custom Controller Factory,看来用于注册控制器的原始方法AddComponentLifeStyleAddComponentWithLifeStyle 现在已弃用:

public class WindsorControllerFactory : DefaultControllerFactory
{
    IWindsorContainer container;

    public WindsorControllerFactory()
    {
        container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));

        // register all the controller types as transient
        var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
                              where typeof(IController).IsAssignableFrom(t)
                              select t;

        //[Obsolete("Use Register(Component.For<I>().ImplementedBy<T>().Named(key).Lifestyle.Is(lifestyle)) instead.")]
        //IWindsorContainer AddComponentLifeStyle<I, T>(string key, LifestyleType lifestyle) where T : class;
        foreach (Type t in controllerTypes)
        {
            container.Register(Component.For<IController>().ImplementedBy<???>().Named(t.FullName).LifeStyle.Is(LifestyleType.Transient));
        }
    }

    // Constructs the controller instance needed to service each request
    protected override IController GetControllerInstance(Type controllerType)
    {
        return (IController)container.Resolve(controllerType);
    }
}

新建议是使用Register(Component.For&lt;I&gt;().ImplementedBy&lt;T&gt;().Named(key).Lifestyle.Is(lifestyle)),但我不知道如何在ImplementedBy&lt;???&gt;() 方法中呈现实现控制器类型。我试过ImplementedBy&lt;t&gt;()ImplementedBy&lt;typeof(t)&gt;(),但是我找不到合适的方法来传入实现类型。有什么想法吗?

【问题讨论】:

  • 请不要将桑德斯的书用于与温莎有关的任何内容,它现在非常已经过时了。
  • @Mauricio Schaffer,我意识到有些东西已经过时了,但是有一些漂亮的宝石可能有用......特别是使用 Web.config 来帮助解决依赖关系:例如提供初始化存储库类所需的连接字符串(尽管我还没有弄清楚如何去做)。
  • 那也过时了,我不建议这样做。
  • @Mauricio,可能没有解决依赖关系,但至少使用连接字符串或数据库名称(在我的另一个问题的示例中)。

标签: asp.net-mvc reflection inversion-of-control castle-windsor


【解决方案1】:

我正在使用ControllerBuilder.SetControllerFactory 和您可以在开源项目MvcContrib 中找到的代码:

Global.asax.cs

protected void Application_Start()
{
    ...

    IWindsorContainer windsorContainer = new WindsorContainer();
    windsorContainer.RegisterControllers(Assembly.GetExecutingAssembly());
    ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(windsorContainer));

    ...
}

WindsorControllerFactory

public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IWindsorContainer _container;

    public WindsorControllerFactory(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException();
        }

        _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException();
        }

        if (!typeof(IController).IsAssignableFrom(controllerType))
        {
            throw new ArgumentException();
        }

        try
        {
            return (IController)_container.Resolve(controllerType);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException();
        }
    }

    public override void ReleaseController(IController controller)
    {
        IDisposable disposable = controller as IDisposable;

        if (disposable != null)
        {
            disposable.Dispose();
        }

        _container.Release(controller);
    }
}

WindsorExtensions(参见MvcContrib

public static class WindsorExtensions
{
    public static IWindsorContainer RegisterController<T>(this IWindsorContainer container) where T : IController
    {
        container.RegisterControllers(typeof(T));

        return container;
    }

    public static IWindsorContainer RegisterControllers(this IWindsorContainer container, params Type[] controllerTypes)
    {
        foreach (Type type in controllerTypes)
        {
            if (ControllerExtensions.IsController(type))
            {
                container.Register(Component.For(type).Named(type.FullName).LifeStyle.Is(LifestyleType.Transient));
            }
        }

        return container;
    }

    public static IWindsorContainer RegisterControllers(this IWindsorContainer container, params Assembly[] assemblies)
    {
        foreach (Assembly assembly in assemblies)
        {
            container.RegisterControllers(assembly.GetExportedTypes());
        }

        return container;
    }
}

ControllerExtensions(参见MvcContrib

public static class ControllerExtensions
{
    public static bool IsController(Type type)
    {
        return type != null
               && type.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)
               && !type.IsAbstract
               && typeof(IController).IsAssignableFrom(type);
    }
}

【讨论】:

  • 所以你不指定ImplementedBy(在RegisterControllers方法中)?没有问题吗?
  • @Lirik: No ;) Castle.Windsor 的扩展方法取自 MvcContrib 项目 (mvccontrib.codeplex.com)。我已经用文件链接更新了我的答案。
  • @Lirik:我将上面的代码与 Castle 2.5.2 和 ASP.NET MVC 3 RTM 一起使用,到目前为止没有问题。不管怎样,我刚刚注意到,MvcContrib.Castle 项目现在用[Obsolete] 标记。自从我上次实施 Windsor 以来,我需要检查是否有更推荐的方法。
  • 看起来真不错!谢谢! :)
【解决方案2】:

您可能还想考虑在最新的 Windsor 版本中使用新的安装程序选项。 Windsor的教程还有更多文档:http://stw.castleproject.org/Windsor.Windsor-tutorial-part-three-writing-your-first-installer.ashx

【讨论】:

    【解决方案3】:

    有一个教程(正在编写中,但 9 部分已经发布)讨论了 Windsor 在 ASP.NET MVC here 中的用法。 据我所知,这是最新的,涵盖了该主题的大部分常用资源。

    【讨论】:

      【解决方案4】:

      @Lirik,作为补充:如果您使用 MVC3,请删除您自己的自定义 IControllerFactory。只需向 Windsor 注册控制器并在其中使用 Windsor 容器实现IDependencyResolver

      将您的IDependencyResolver 设置为MVC DependencyResolver,DefaultControllerFactory 将自动连接在容器中注册的控制器(通过DependencyResolver)。

      【讨论】:

      • 对不起,我该如何设置DependencyResolver? (请注意,我仍在阅读这本书和学习,所以如果很明显,我很抱歉)我从头开始制作这个项目......我希望在哪里看到“我自己的”IControllerFactory?我在Application_Start 方法中设置了WindsorControllerFactory,但在我设置WindsorControllerFactory 之前没有设置其他IControllerFactory
      • 我试图在IDependencyResolversome people suggest that we shouldn't implement one for Windsor 上获取一些信息。
      • @Lirik,您甚至不需要自己实现 IDependencyResolver。 CommonServiceLocator可以设置为DependencyResolver。只需将Windsor adapter for CSL 设置为:DependencyResolver.SetResolver(new WindsorServiceLocator(windsorContainerInstance)); 您无需设置控制器工厂,因为默认情况下DefaultControllerFactory 是开箱即用的。
      • @Lirik,关于 DependencyResolver 和 Windsor。 Mike Hadlow 谈论的问题似乎与容器以及它如何跟踪瞬态实例的生命周期有关,而不是与 DependencyResolver 本身有关。我主要使用StructureMapNinject 并且从来没有遇到过这个问题。但是,如果 Windsor 存在问题,您可以按照 Mike 帖子中的 cmets 所述,在 IoC 容器中注册控制器时明确定义控制器的生命周期。
      • @Lirik,我发现了更多关于这个问题的信息。这似乎是对象本身的问题,而不是任何类型的 IoC 容器或 DependencyResolver。即使您根本不使用 IoC 容器或解析器,您也可能存在内存泄漏。如果您的情况有些特殊,并且您有自己的 IController 实现不是 IDisposable - 您对 Mike 谈到的所有这些内存问题持开放态度。默认 ControllerBaseController 类是 IDisposable 所以 MVC 应该在不再需要它时处理它。
      【解决方案5】:

      类似:

      public void Register(IWindsorContainer container)
          {
              Assembly.GetAssembly(typeof(ControllersRegistrarMarker)).GetExportedTypes()
                      .Where(IsController)
                      .Each(type => container.AddComponentLifeStyle(
                                            type.Name.ToLower(),
                                            type,
                                            LifestyleType.Transient));
          }
      

      ControllersRegistrarMarker 只是控制器程序集中的一个空类

      【讨论】:

      • AddComonentLifeStyle 已弃用...我可以使用它,但我宁愿使用与弃用函数相关的注释所建议的 container.Register(...) 函数。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多