使用IOC Container创建存储库层单例

问题描述 投票:2回答:2

我公司的MVC解决方案使用IOC容器将Caching / Repository层注入控制器。这是非常昂贵的,因为目前我们每次创建控制器时都会生成新类(当缓存层对象已经引用了repo层时,可以达到数千个对象 - 并且所有这些类都被创建)。我知道Singleton模式由于很多原因而非常不受欢迎(参见Why Singletons are Evil)但是有没有理由不将IOC Container设置为缓存/ repo层对象的单例?

谢谢。

c# singleton ioc-container
2个回答
6
投票

你搞混了。 Singleton设计模式与DI库使用的Singleton生活方式完全不同。

使用Singleton模式,您通常在具体类上定义一个公共静态只读字段,该类保存该类的唯一实例;此实例由类本身创建,整个应用程序可以访问该只读字段。例如。:

public static class CarEngine
{
    public static readonly CarEngine Instance = new CarEngine();

    // class methods
}

使用Singleton生活方式,您可以指示容器在该容器的生命周期内只创建一个实例并重复使用它。

Singleton设计模式是一个问题,因为它迫使消费者对具体类(依赖性反转原则违规)采取硬依赖,并且由于该具体类在内部控制创建,因此在使用期间使用虚假实现变得更加困难。测试。除此之外,由于消费者没有需要该类作为依赖关系的构造函数,因此有效地使读取代码的人隐藏依赖关系,创建测试,并从工具中作为可以为您进行对象图分析的DI库。你指出的The article确实在解释为什么Singleton设计模式不好时确实做得很好。

然而,文章从未真正提到过Singleton的生活方式,但由于它讨论了Singleton设计模式如何隐藏依赖关系,因此它意味着应该注入依赖关系。并且由于您希望某些类具有一个实例并通过构造函数注入它们,因此Singleton生活方式是此问题的实际解决方案。

Singleton生活方式解决了这些问题,因为您从具体类中移除了创建该单个实例的责任,这允许消费者在其构造函数中依赖抽象,这使得依赖性可见并且代码更易于测试。

因此,使您的注册单身人士没有任何问题。事实上,我认为你应该选择尽可能多的注册单例,因为这可以防止开发人员在实现依赖注入时通常面临的大量问题。通过使每个组件都不可变和无状态,它们变得更容易推理,并且您可以防止自己意外地将运行时数据注入到组件中,即bad practice。 DI的另一个常见缺陷是Captive Dependencies,这意味着一个组件依赖于另一个应该具有较短生活方式的组件。如果使所有组件都是不可变的,无状态的和单独的,那么Captive Dependencies的问题就会消失,因为单例组件可以安全地相互依赖。

当然,您总是需要组件中的运行时数据(例如请求数据,O / RM上下文等),但可以在运行时通过将提供程序或简单的Func<DbContext>注入到抽象第三方工具的适配器实现中来请求这些数据。从您的应用程序(如果您遵循SOLID,这是一个很好的做法)。这个Stackoverflow answer详细介绍了这一点。


1
投票

首先。 .NET可以在很短的时间内创建数百万个对象。使用IoC有点慢,但不是那么慢。 benchmark测试了一些IoC,他们在几秒钟内解决了50万个物体。

我公司的MVC解决方案使用IOC容器将Caching / Repository层注入控制器。这是非常昂贵的,因为目前我们每次创建控制器时都会生成新类(当缓存层对象已经引用了repo层时,可以达到数千个对象 - 并且所有这些类都被创建)。

您是说每次请求都会重新生成缓存吗?然后你做错了什么。

缓存通常是单个实例(即,由容器创建一次,然后每次解析缓存时返回相同的实例)。

存储库必须是每个请求,因为它需要数据库连接和/或事务。你想要保持这些短暂的生活。

所以问题不在于IoC,而在于您如何设计缓存,存储库和缓存对象之间的交互。

很抱歉没有清楚解释。我们使用dapper作为Repo层,因此Repo类仅在调用方法时创建与数据库的连接。缓存使用HTTPCache和Redis,但我们有缓存Repo类来处理逻辑(如果在缓存中..)如果每个用户调用创建数千个重复对象,似乎它可以不必要地对服务器资源征税

我还没跟你在一起。 imho缓存实体是持久层中的实现细节。

  1. 您的存储库是作用域(每个http请求创建一个对象)或瞬态(每次都是新对象)
  2. 您的缓存是SingleInstance(每个应用程序生命周期在对象上创建)
  3. 您的存储库包含对缓存的引用(构造函数注入)
  4. 当您的存储库无法在缓存中找到该对象时,它将查询该数据库

为什么缓存只能在存储库中使用(除了作为持久性实现细节)?因为它降低了复杂性,因为存储库是系统中唯一知道所有实体修改而没有额外逻辑的类。

© www.soinside.com 2019 - 2024. All rights reserved.