【问题标题】:What is the best way to inject repositories into an ASP.NET controller将存储库注入 ASP.NET 控制器的最佳方法是什么
【发布时间】:2013-08-14 17:35:23
【问题描述】:

我们有一个用 ASP.NET MVC 编写的项目,我们使用 NInject 将存储库注入到控制器中。目前我们正在使用属性和 Inject-attribute 来注入存储库,效果很好:

[Inject]
public IMyRepository MyRepos {get;set;}

另一种注入方式是使用NInjectServiceLocator“手动”进行注入:

var myRepos = NInjectServiceLocatorInstance.Resolve<IMyRepository>();

现在我想知道以下几点:第一种方法要求所有存储库都列在控制器的顶部(当然不一定在顶部,但它是最合乎逻辑的位置)。每当发出请求时,NInject 都会实例化每个存储库。无论在特定操作中是否确实需要所有存储库,都会发生这种情况。

使用第二种方法,您可以更精确地控制实际需要哪些存储库,因此这可能会在创建控制器时节省一些开销。但您可能还必须包含代码以在多个位置检索相同的存储库。

那么哪一个会更好呢?是只拥有一堆存储库属性更好,还是在需要它们的时间和地点解决特定操作实际必需的存储库更好?注入“无用”存储库是否涉及性能损失?那里有(甚至;-)更好的解决方案吗?

【问题讨论】:

  • 调用NInjectServiceLocatorInstance.Resolve 是一种称为服务定位器的模式。在 Stackoverflow 和互联网上已经写了很多关于这个的文章(例如这是一个famous article)。
  • @Steven:我知道这种模式,而且它也有它的优点和缺点。正如我所说,使用服务定位器可能仍然不是最好的解决方案,但在这种情况下它会比属性或构造函数注入更好吗?
  • 这是你应该进行注入的顺序:构造函数注入、属性注入、工厂、方法注入、服务定位器。仅当上一个选项不合适时才选择下一个选项。
  • @Steven 定义“适当”。当特定操作只使用一个时,在构造函数中定义五个存储库会更好吗?或者更好(也许从性能的角度来看,也许从可读性的角度来看)使用服务定位器并向 NInject 询问实际需要的所需存储库?
  • “合适”的意思是:如果你能做到,你就应该做到!注入在运行时并不总是使用的服务应该不是问题,因为对象图的构建应该非常快(这意味着:除了存储引用之外,不要在构造函数中做任何事情)。如果注入超过 5 个依赖项,则可能违反了单一职责原则。这将导致维护问题。如果你需要延迟类型的创建,你不能使用ctor和prop注入,但你不应该使用服务定位器,因为你可以使用工厂。

标签: asp.net-mvc ninject


【解决方案1】:

我更喜欢构造函数注入:

private readonly IMyRepository _repository;

public MyController(IMyRepository repository)
{
    _repository = repository;
}
  • 您的所有依赖项都在一个操作中列出
  • 您的控制器不需要了解有关 NInject 的任何信息
  • 您可以在不涉及 NInjects 的情况下通过将接口直接存根到构造函数来对控制器进行单元测试
  • 控制器的代码更简洁

NInject 或任何其他 DI 框架将在幕后完成工作,让您专注于实际问题,而不是 DI。

【讨论】:

  • 这与我现在使用的属性有相同的缺点。唯一的区别是现在所有的存储库都需要在构造函数中。
  • 确实如此,但通过管理您的存储库范围 (InRequestScope),例如,每个请求只会创建一次它们。在我看来,代码的简洁性和可维护性胜过不必要的对象创建。如果您的存储库实际上是纯存储库,那么对象创建开销将非常小。我遇到了同样的问题 (stackoverflow.com/questions/18203401/…),并认为这是一个微优化。
  • 当然,所有注入都在请求范围内。你可能是对的,这是在性能方面的微观优化。但它实际上可能使代码更具可读性,因为存储库只在实际需要的地方注入。
  • 构造函数注入它始终是向我注入依赖项的最干净的方式 如果您不是开发人员,属性注入器会有些晦涩 使用属性集注入时很难知道需要哪些依赖项以及需要什么不是!
【解决方案2】:

Constructor Injection 应该是您使用 DI 时的默认选择。

您应该问自己,控制器是否真的依赖于特定的类才能工作。

如果您只有需要依赖的特定方法,也许Method injection 也可能是特定场景的解决方案。

我从未使用过Property Injection,但 Mark Seeman 在他的书中描述了它 (Dependency Injection in .NET):

PROPERTY INJECTION 仅应在您正在开发的类具有良好的 LOCAL DEFAULT 并且您仍然希望使调用者能够提供不同的实现 类的依赖关系。

当 DEPENDENCY 是可选的时,最好使用 PROPERTY INJECTION。

注意 关于财产注入的问题存在一些争议 表示可选的依赖项。作为一般的 API 设计原则,我 考虑属性是可选的,因为您很容易忘记分配 他们和编译器不会抱怨。如果你接受这个原则 一般情况下,在DI的特殊情况下也必须接受。 4

一个本地默认值被描述为:

在同一个程序集中定义的抽象的默认实现 消费者。

除非您正在构建 API,否则我建议不要使用属性注入

每当发出请求时,NInject 都会实例化每个存储库。无论在特定操作中是否确实需要所有存储库,都会发生这种情况。

我认为你不用太担心使用构造函数注入时的性能

【讨论】:

  • 这些言论对我来说确实有意义,但我想知道在这种情况下,如果您只要求在实际需要的地方注入必要的存储库并且不在构造函数中或作为属性。
  • 我不认为可读代码取决于您如何实现 DI,DI 只是在您的应用程序中创建松散耦合的一种方式,因此您的代码更易于维护。如果以正确的方式实施
  • 请注意,Ninject wiki 明确声明不支持 Mark Seeman 的书(.net 中的依赖注入)中描述的属性注入。
【解决方案3】:

到目前为止,我最喜欢的方法是:

public class MyController : Controller
{
    public IMyRepository MyRepos {get;set;}

    public MyController(IMyRepository repo)
    {
        MyRepos = repo;
    }
}

因此您可以使用 NuGet 包,例如 Ninject.MVC3(或 MVC4),它特别支持在 MVC 自己的 IoC 类中包含 Ninject 内核

https://github.com/ninject/ninject.web.mvc/wiki/MVC3

一旦你加入了 Ninject 钩子,你就可以让它将实例注入到控制器的构造函数中,我认为这样更简洁。

编辑:

啊,好的。更彻底地阅读了您的问题后,我知道您将要解决的问题。简而言之,如果您想选择实例化哪些 repo 类,则需要手动调用,例如:

var myRepos = NInjectServiceLocatorInstance.Resolve&lt;IMyRepository&gt;();

您不能将 Ninject(或任何其他 IoC AFAIK)配置为根据当前执行方法选择性地创建对象实例。我觉得这种粒度级别是一个真正的边缘情况,可以通过编写自己的控制器工厂类来解决,但这有点过头了。

【讨论】:

  • 这与我现在使用的属性有相同的缺点。唯一的区别是现在所有的存储库都需要在构造函数中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
  • 2018-08-30
  • 2010-12-17
  • 1970-01-01
  • 2020-08-26
相关资源
最近更新 更多