如何在ASP.NET MVC中避免使用CastleWindsor的服务定位器/实现服务定位器

时间:2015-10-03 18:09:46

标签: asp.net-mvc dependency-injection castle-windsor castle service-locator

我使用CastleWindsor作为我的依赖注入框架,当你在Controller中时它们都运行良好,因为我们可以使用controllerfactory构造函数注入。

但是有一些特定的情况,依赖注入(构造函数注入)将无法工作。例如:我希望能够在一些实用程序类或扩展方法(例如HtmlHelper)中使用IOC来解析我的依赖关系我的观点。我知道有些人不会就此达成一致,而是保持观点愚蠢,但让我们不要讨论。

所以这基本上给了我一个选项,并且使用了...服务定位器。所以我知道服务定位器被大多数人视为反模式,我明白为什么......但是如果你不能使用依赖注入,你如何解决与IOC的依赖关系?对于我所知道的一切,使用IOC的服务定位器比没有任何东西更好。我想避免服务定位器模式,但我似乎不明白在某些特定情况下如何避免它。

接下来的问题是..所以即使你喜欢/不喜欢服务定位器。哪个是使用CastleWindsor实现此功能的最佳选择?

所以我猜选项将是:

  1. 将容器公开为全局对象(或通过包装容器的其他全局对象),您可以从代码中的任何位置检索该容器。然后,您可以在容器上调用resolve和release方法。我不喜欢这样的一件事是,我们必须明确地为瞬态生活方式对象打电话。如果缺乏经验的开发人员不这样做,您最终会发生内存泄漏。

  2. 我还发现:https://www.nuget.org/packages/CommonServiceLocator.WindsorAdapter并且它有很多下载..(与选项1相同但更通用并包装容器,因此您可以轻松交换DI框架)我查看了代码,我发现适配器只有解析对象的方法。所以我有点想知道为什么没有发布方法..这是否意味着这个包有瞬态生活方式对象的内存泄漏问题?

  3. 希望有人能就这些问题给我一些建议!

1 个答案:

答案 0 :(得分:5)

  

如何避免在静态扩展方法(HTML Helpers)中使用服务定位器?

首先,尽可能避免使用静态扩展方法。当你有一些不太可能改变但没有依赖关系的逻辑(除了它们适用的类/接口之外)时,静态扩展方法很有效。当然,这并不总是可行,但如果你可以使用另一种方法,它确实会减少这种情况。

HTML助手

  

2017/01/30更新:将DI与HTML帮助器一起使用的更好方法是使用this answer中的抽象工厂,它允许对HTML进行DI容器生命周期管理帮助依赖。

当您确实需要使用静态方法时,注入依赖项的一种方法是使用属性注入。

public static class MyHtmlHelperExtensions
{
    private static IHtmlHelperService htmlHelperService;

    // Property for use with dependency injection in the composition root
    public static IHtmlHelperService HtmlHelperService
    {
        set
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (htmlHelperService != null)
                throw new ArgumentExeption("HtmlHelperService cannot be set twice");
            htmlHelperService = value;
        }
    }

    // The static method simply calls the instance method of our service,
    // but does not contain any logic.
    public static MvcHtmlString MyHtmlHelper(this HtmlHelper htmlHelper)
    {
        return htmlHelperService.MyHtmlHelper(htmlHelper);
    }
}

基本上,静态HTML帮助程序只是一个外观,它将其方法委托给Aggregate Service,其中包含执行实际工作的实际服务。

在构建DI容器之后,应用程序启动时会注入HtmlHelperService,但仍然在应用程序的composition root内。

// DIConfig.Register() will create the container and register all of our type mappings.
var container = DIConfig.Register();

// While we are still in the composition root, we instantiate and 
// assign our HtmlHelperService along with its dependency graph.
MyHtmlHelperExtensions.HtmlHelperService = container.Resolve<IHtmlHelperService>();
  

注意:在MVC6中,将会有view components,它更像控制器而非静态HTML帮助程序,以消除创建具有依赖关系的静态HTML帮助程序的需要。

属性

对于导致服务定位器的另一个常见的依赖注入问题来源Attributes,最好的方法是define the attribute with no behavior,这可以通过拆分ActionFilterAttribute派生类型来完成进入一个愚蠢的人属性和在组合根中解析的DI友好全局操作过滤器。请参阅this MVC IActionFilter examplethis MVC AuthorizeAttribute example

根本没有理由认为属性需要定义行为,因为属性只定义了可以由另一个服务(具有行为)探索的元数据。因此,属性永远不需要依赖项,只有包含行为的服务才需要。