MVC DI / IoC有太多依赖关系?

时间:2013-01-04 12:09:32

标签: asp.net-mvc dependency-injection

给出MVC控制器的这个例子;伪代码......

PostController
{
    private IGetPost _getPost;
    private ICreatePost _createPost;
    private IDeletePost _deletePost;
    private IUpdatePost _updatePost;

    public ActionResult Get()
    {
        return _getPost();
    }

    public ActionResult Create(post)
    {
        return _createPost(post);
    }

    public ActionResult Update(posts)
    {
        return _updatePost(post);
    }

    public ActionResult Delete(post)
    {
        return  _deletePost(post);
    }
}

我的问题是,如果调用控制器上的任何一个操作,控制器的所有依赖关系都会为它们创建实例,这看起来像是一个性能buzzkill。有一个更好的方法吗?我唯一的想法是创建4个不同的控制器,每个控制器只有一个动作,但这似乎有点矫枉过正。我还想过在每个动作中直接调用DependencyResolver,但我不确定如果我这样做会对单元可测试性产生什么影响。

2 个答案:

答案 0 :(得分:1)

为什么这是一个表演的口号?你测量这是不是这样的?虽然在确定哪些实现映射到哪些接口时存在开销,但大多数值得他们使用的dependency injection(DI)容器将执行此映射一次(并且通常在运行时创建编译代码以便查找尽可能快地创建映射。

这只是创建对象的问题,与使用new keyword时没有什么不同。

对于一般情况,这不会受到性能影响。考虑到这是一个Web应用程序,如果您获得Stack Overflow流量级别,那么很好可能成为缩放的障碍;这些操作中的每一个都很便宜,但是当乘以百万分之一时,总的来说,它非常非常昂贵,而且通常,这些是可能导致资源争用等的事物类型。

假设情况并非如此(流量的堆栈溢出级别),那么 可能面临性能问题的是构造函数的实现

如果这四个接口(或任何数量的接口)的实现成本很高,那不是DI的功能,它是代码的函数,你可以从优化中获得更多好处

调整依赖注入可能的唯一好处是,如果构造中的一个或多个实现具有高开销,并且您的DI容器创建一个 所有接口实现的实例,而不是每个接口实现的一个实例。但是,当您确定可以使用该选项时,您应通过DI层查看生命周期管理(意味着,为该服务所有请求提供该类的一个实例是可行的;是否为线程-safe?它能持续很长时间吗?等等。)

如果你真的关心这个并且以上不适用或者不是一个选项,那么是的,你可以创建一些较小的控制器,这可能因其他原因而有意义;如果您执行的操作在逻辑上彼此不相关,则他们可能位于不同的控制器中。

然而,看看你所采取的行动,你所拥有的逻辑鸿沟似乎是正确的。

长话短说,不要尝试在没有测量它的地方优化性能。

也就是说,无论你做什么,解决你班级内的依赖关系。如果你这样做,你将失去DI的所有好处,并且将你的类紧密绑定到DI容器(并且可能会在其中杀死可测试性)。

答案 1 :(得分:0)

我不确定性能是否真的会成为一个问题,除非你有一个非常荒谬的依赖项。无论哪种方式,看看你的建筑都是一个很好的练习。

我们这样做的方法是将服务级别调用抽象到外部类,称为处理程序。这来自Project Silk的灵感。这样,您不仅可以减轻控制器的负担,使其更易于管理,而且可以避免这种依赖性的增加。

在这种情况下,您将拥有一个控制器,对于每个操作,只需解析处理程序及其依赖项,然后执行它。处理程序被定义为具有极少(一种常见)方法的小类。

class MyController : BaseController
{
    public ActionResult Get(int id)
    {
        return Using<GetItemHandler>().Execute(id);
    }
}

class GetItemHandler
{
    private IDependency _myDep;

    public GetItemHandler(IDependency dep)
    {
        _myDep = dep;
    }

    public Execute(int id)
    {
        return _myDep.Get(id);
    }
}

请注意,它的工作方式是Using<T>()方法(在BaseController中定义)使用IoC容器来解析处理程序,从而获取它的所有依赖项。然后我们可以正常使用它。我认为这种模式确实有助于分离责任,让你的班级和控制员保持良好和精益。