优雅地减少ASP.NET MVC控制器中的依赖项数量

时间:2009-03-17 11:09:35

标签: asp.net-mvc dependency-injection dependencies inversion-of-control

我们正在开发正在成为一个相当大的ASP.NET MVC项目,并且代码气味开始抬头。

每个控制器都有5个或更多依赖项,其中一些依赖项仅用于控制器上的1个操作方法,但显然是为控制器的每个实例创建的。

我正在努力想办法减少90%的电话不必要地创建的对象数量。

以下是我正在考虑的一些想法:

  1. 将控制器拆分为更小,更有针对性的控制器。
    • 目前我们每个域实体大致有一个控制器,这导致了我们想要模仿的漂亮的URL,这意味着我们最终会得到一个更复杂的路由方案。
  2. 传入包装IoC容器的接口。
    • 这意味着只有在明确要求时才会创建对象。然而,这似乎就像在猪身上涂上口红。
  3. 以某种方式扩展框架,实现两者的疯狂结合。
  4. 我觉得其他人一定遇到过同样的问题;所以你是如何解决这个问题的,或者你是否只是忍受它,因为它在你眼中并不是那么大的问题?

4 个答案:

答案 0 :(得分:3)

我一直在思考这个问题的解决方案,这就是我想出来的:

直接将依赖项注入控制器操作,而不是注入控制器构造函数。这样你只需要注入你需要的东西。

我真的只是把它鞭打了,所以它有些天真并没有在愤怒中测试,但我打算尽快实现这个尝试。建议欢迎!

它当然特定于StructureMap,但您可以轻松使用不同的容器。

在global.asax中:

protected void Application_Start()
{
    ControllerBuilder.Current.SetControllerFactory(
            new StructureMapControllerFactory());
}

这里是structuremapcontrollerfactory:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            var controller = 
                    ObjectFactory.GetInstance(controllerType) as Controller;
            controller.ActionInvoker = 
                    new StructureMapControllerActionInvoker();
            return controller;
        }
        catch (StructureMapException)
        {
            System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
            throw;

        }
    }
}

和structuremapcontrolleractioninvoker(可以做得更聪明一点)

public class StructureMapControllerActionInvoker : ControllerActionInvoker
{
    protected override object GetParameterValue(
            ControllerContext controllerContext, 
            ParameterDescriptor parameterDescriptor)
    {
        object parameterValue;
        try
        {
            parameterValue = base.GetParameterValue(
                    controllerContext, parameterDescriptor);
        }
        catch (Exception e)
        {
            parameterValue = 
                    ObjectFactory.TryGetInstance(
                            parameterDescriptor.ParameterType);
            if (parameterValue == null)
                throw e;
        }
        return parameterValue;
    }
}

答案 1 :(得分:1)

“服务定位器”的概念已被添加到像Prism这样的作品中。它具有减少开销的优势。

但是,正如你所说,它只是隐藏在地毯下的东西。依赖关系不会消失,你只是让它们不那么明显,这违背了使用DI的目标之一(明确说明你所依赖的东西),所以我要小心不要过度使用它。

也许你可以通过委派一些工作来获得更好的服务。如果您有某种方式想要重新分区控制器,您可能只想创建该类并让控制器通过DI获取它的实例。 它不会降低创建成本,因为依赖关系仍然会在创建时解决,但至少你会按功能隔离这些依赖关系并保持路由方案的简单。

答案 2 :(得分:1)

我会单独考虑依赖创建依赖对象的问题。依赖只是控制器源代码引用的事实某种类型。这会降低代码复杂性,但是没有运行时成本。另一方面,对象的实例化具有运行时成本。

减少代码依赖性数量的唯一方法是分解控制器。正如你所说,其他任何事情只会使依赖关系变得更漂亮。但是,依赖性(与依赖对象的实例化相反,我将在一秒钟内覆盖)更漂亮可能就足以成为一个解决方案,您不需要分解控制器。因此,我认为IoC是一个不错的解决方案。

Re:创建对象,你写,“......其中一些依赖项仅用于控制器上的一个动作方法,但显然是为控制器的每个实例创建的。”这让我觉得真正的问题,而不是依赖,本身。因为随着项目的扩展,它只会变得更糟。您可以通过更改对象的实例化来解决此问题,以便在需要它之前不会发生。一种方法是使用具有延迟实例化的属性。另一种方法是使用模型绑定器将操作方法​​的参数用于实例化所需的对象。另一种方法是编写返回所需实例的函数。如果不知道你正在使用的对象的目的,很难说哪种方式最好。

答案 3 :(得分:1)

您的控制器可能变得太“胖”。我建议创建一个位于控制器下方的应用程序层。应用程序层可以封装控制器操作内部的许多业务流程,并且更加可测试。它还可以帮助您组织代码,而不受指定操作方法的限制。

使用ServiceLocation也会有所帮助(是的,我基本上重申了Denis Troller的答案 - 这可能并不好,但我确实投了他的答案)。