3

给定这个 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);
    }
}

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

4

2 回答 2

1

为什么它是性能嗡嗡声?你有测量过这种情况吗?虽然在确定哪些实现映射到哪些接口时存在开销,但大多数值得称道的依赖注入(DI) 容器将执行一次此映射(并且通常动态创建编译代码,以便尽可能快地查找)以创建映射.

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

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

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

如果这四个接口(或任意数量的接口)的实现成本很高,那不是 DI 的功能,而是您的代码的功能,您将从优化中获得更多好处。

调整依赖注入可能有益的唯一地方是,如果构建这些实现中的一个或多个具有高开销,并且您让 DI 容器为所有接口实现创建一个实例,而不是为每个接口实现创建一个实例。但是,只有当您确定该选项可供您使用时,您应该通过 DI 层查看生命周期管理(这意味着,拥有该类的一个实例来服务所有请求是可行的;它是线程安全的吗?它是否坚持很长一段时间的任何资源?等)

如果您真的对此感到担忧并且上述内容不适用或不是一个选项,那么是的,您可以创建一些较小的控制器,这可能出于其他原因而有意义;如果您正在执行的操作在逻辑上彼此不相关,则它们可能应该位于单独的控制器中。

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

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

也就是说,无论您做什么,都不要解决类内部的依赖关系。如果这样做,您将失去 DI 的所有好处,并且将您的类与 DI 容器紧密绑定(并且可能会在使用时破坏可测试性)。

于 2013-01-04T12:20:00.017 回答
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 容器来解析处理程序,从而获取它的所有依赖项。然后我们就可以正常使用了。我认为这种模式确实有助于分离职责并使您的类和控制器保持良好和精简。

于 2013-01-04T12:20:33.327 回答