延迟依赖注入

时间:2012-09-05 15:09:42

标签: c# .net design-patterns inversion-of-control ninject

我有一个项目,其中Ninject用作IoC容器。我担心的是很多类都有这样的构造函数:

[Inject]
public HomeController(
    UserManager userManager, RoleManager roleManager, BlahblahManager blahblahManager) {
   _userManager = userManager;
   _roleManager = roleManager;
   _blahblahManager = blahblahManager;
}

如果我不想同时拥有这些课程的所有实例怎么办?

当所有这些类被Lazy<T>包装并传递给构造函数的方式并不是我所需要的。尚未创建T个实例,但Lazy<T>实例已存储在内存中。

我的同事建议我使用Factory模式控制所有实例,但我不确定IoC是否有这么好的设计错误。

这种情况是否有解决办法,或者IoC在设计中确实存在如此大的缺陷?也许我应该使用另一个IoC容器?

有什么建议吗?

3 个答案:

答案 0 :(得分:41)

我觉得你在做premature optimization:不要这样做。

服务的构造函数应该nothing more而不是存储私有字段中的依赖项。在这种情况下,创建这样的对象实际上是轻量级的。不要忘记.NET中的对象创建速度非常快。在大多数情况下,从性能的角度来看,这些依赖关系是否被注入并不重要。特别是在与对象数量进行比较时,应用程序的其余部分(以及您使用的框架)正在吐出。实际成本是当您开始使用Web服务,数据库或文件系统(或一般的I / O)时,因为它们会导致更大的延迟。

如果创建非常昂贵,通常应该将创建隐藏在Virtual Proxy后面,而不是在每个消费者中注入Lazy<T>,因为这样可以使常见的应用程序代码无视这里的事实是一种延迟创建的机制(当你这样做时,你的应用程序代码和测试代码变得越来越复杂)。

Dependency Injection: Principle, Practices, Patterns的第8章包含有关延迟和虚拟代理的更详细讨论。

但是,Lazy<T>只占用20个字节的内存(另一个24 bytes用于其包裹的Func<T>,假定为32位进程),并创建Lazy<T>实例几乎是免费的。所以没有必要担心这个问题,除非你在一个内存严重受限的环境中。

如果内存消耗有问题,请尝试注册寿命大于瞬态的服务。您可以针对每个Web请求或单个请求执行每个请求。我甚至会说,当你在一个创建新对象的环境中出现问题时,你应该只使用单一服务(但是你不太可能在这样的环境中工作,因为你正在构建一个web应用程序)

请注意,Ninject是.NET中较慢的DI库之一。如果那令你不安,switch to a faster container。一些容器的性能接近于手动创建对象图。 但是,无论如何,要对此进行分析,许多开发人员出于错误的原因切换DI库。

请注意,使用Lazy<T>作为依赖项是leaky abstraction(违反Dependency Inversion Principle)。有关详细信息,请阅读this answer

答案 1 :(得分:1)

您还可以使用以下语法将其插入到action方法中。 (我不确定确切介绍了哪个版本)。

构造函数是最佳实践,但是当我有一个服务 进行了一些昂贵的初始化时(实际上是偶然地),我不得不故意这样做一次,但是一段时间以来没有发现它,只是最简单地将其移动到 did 需要它的一种方法。

如果您只需要从一个操作方法访问服务,那么这可以使代码更简洁-但请记住,如果将其注入该方法,则必须将其传递到任何地方,因为它不再位于{ {1}}。绝对不要在操作方法中分配给this-这太可怕了。

this.service

https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/dependency-injection?view=aspnetcore-2.2#action-injection-with-fromservices

答案 2 :(得分:0)

史蒂文说这看起来像过早的优化是正确的。这些物体的构造非常快,通常不会成为瓶颈。

然而,使用Lazy来表达依赖关系,您不需要立即使用它是依赖注入框架中的常见模式。 Actofac是一个内置支持various wrapping types的容器。我确定Ninject也有一个扩展,也许看看这个,Ninject Lazy