【问题标题】:Does IoC with constructor injection in asp.net mvc controllers waste resources?在 asp.net mvc 控制器中使用构造函数注入的 IoC 会浪费资源吗?
【发布时间】:2013-03-23 14:07:15
【问题描述】:

我不确定是不是只有我一个人,但我感觉 ASP.NET MVC 控制器中使用的构造函数注入会导致不必要的资源消耗。

在创建控制器时仍需要创建未用于特定网络请求的组件。 这就像在我渴了牛奶的时候买了牛奶和果汁,然后我就把果汁扔掉了。

比较控制器的构造函数注入和服务定位器的这些示例,以澄清我的担忧。

Constructor Injection,创建了booth deps但只使用了一个。

public class MyController : Controller
{
    private readonly IDep1 _dep1;
    private readonly IDep2 _dep2;

    public MyController(IDep1 dep1, IDep2 dep2)
    {
        _dep1 = dep1;
        _dep2 = dep2;
    }

    public ActionResult Index()
    {
        _dep1.MakeStuff();
        return View();
    }
    public ActionResult PageTwo()
    {
        _dep2.MakeStuff();
        return View();
    }
}

Service Locator,每个dep只有在使用时才会创建。

public class MyController : Controller
{
    public ActionResult Index()
    {
        var dep1 = ServiceLocator.Resolve<IDep1>();
        dep1.MakeStuff();
        return View();
    }
    public ActionResult PageTwo()
    {
        var dep2 = ServiceLocator.Resolve<IDep2>();
        dep2.MakeStuff();
        return View();
    }
}

请注意 IoC 容器(由于许多原因是有益的)仍然可以用于服务定位器模式。我不希望这是围绕 IoC 和容器框架的讨论,也不是构造函数注入的其他好处(依赖关系的清晰可见性等)。我关心的是构造函数注入模式以及它在 ASP.NET MVC 控制器情况下如何浪费资源

我想这里的主要问题是: 对于上述场景(ASP.NET MVC 控制器)而言,Service Locator 是更好的解决方案性能吗?

【问题讨论】:

  • 在大多数(如果不是所有)IoC 容器中,接口类型和具体类型都被存储,并且只有在检测到依赖关系时才会真正创建类型,例如对类型具有依赖关系的构造函数。
  • 没错,所以在第一个示例中,展位依赖项是由 IoC 容器创建的,并且只使用了一个,对吧?
  • 我想说,在这种情况下,可测试性远远超过了性能提升,正如@motime 所说,如果您的瓶颈在于创建依赖项(并且他们没有进行基于 I/O 的操作)或其他繁重的工作)。你在一个好地方。
  • 你在构造函数中到底在做什么?根据 .NET Framework 设计指南: 在构造函数中做最少的工作。除了捕获构造函数参数之外,构造函数不应该做太多工作。任何其他处理的成本应延迟到需要时。如果您这样做,那么您不必担心资源消耗。
  • 我不是在谈论我的具体问题。这是关于设计和后果的一般问题。假设一个控制器有几个方法,每个方法使用 4 个不同的域服务实例 => 16 个对象,其中只有 4 个用于特定请求(操作方法)=> 12 个不必要的实例。这 12 个对象本身可能有一个图表,比如另外两个级别和每个级别上的 3 个实例 => 12 个 => 108 个实例创建中的每一个都额外增加 9 个,这是不必要的。在某些情况下(短时间内大量 Web 请求)会影响性能/利用率。没有?

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


【解决方案1】:

如果你不想构造你不需要的东西/使用可以构造你需要的东西。

在我们的控制器工厂中使用服务定位器 + DI,我们可以将一个 dep 工厂传递给控制器​​,并允许控制器决定向工厂询问什么。

这不是我们有工厂模式的原因吗?

因此,要在问题中重构您的示例,您可能想做这样的事情......

public class MyController : Controller
{
    private readonly IDepFactory _factory;

    public MyController(IDepFactory factory)
    {
        _factory= factory;
    }

    public ActionResult Index()
    {
        var dep1 = factory.Get<IDep1>();
        dep1.MakeStuff();
        return View();
    }
    public ActionResult PageTwo()
    {
        var dep2 = factory.Get<IDep2>();
        dep2.MakeStuff();
        return View();
    }
}

这既高效又很好地利用了模式。

从测试的角度来看,您可以模拟传入的 IDepFactory,以便它始终返回一些常量,当查询返回已知结果时,您可以确认控制器中的逻辑运行正常。

从性能的角度来看,我认为差异主要取决于每种情况下依赖堆栈的深度,如果依赖堆栈,您可能更喜欢不使用工厂并像以前一样传入两个 deps非常浅,因为收益几乎没有。

【讨论】:

    【解决方案2】:

    如果对象创建是您的瓶颈,那么您要么处于非常好的情况(其他一切都像魅力一样工作,因此

    Mark Seemann 已经在这里介绍了这个主题: http://blog.ploeh.dk/2011/03/04/Composeobjectgraphswithconfidence/

    在许多情况下,这是您必须承受的性能损失,因为您 无论如何都需要这些课程,但有时您可能会担心 过早地接受这种表现。但是,我声称 在绝大多数情况下,这种担忧是无关紧要的。

    并提供了一个可能的解决方案,如果它对您仍然很重要(延期分支)。

    【讨论】:

    • 我同意可测试性问题。我也同意对象创建时间对性能的最小影响。尽管如此,在某些情况下(大量 Web 请求、服务于多个操作的控制器),这将是一个优化点。对吗?
    • @MaxWikström,在某些情况下,当然可以。但通常情况下,这将是错误设计决策的标志。您应该启发您的构造函数将初始化您的依赖项,仅此而已,应该推迟繁重的操作。另外,如果你初始化了太多(3 是我的幻数)依赖项,我也认为这是糟糕设计的标志。
    猜你喜欢
    • 1970-01-01
    • 2016-06-18
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    相关资源
    最近更新 更多