使用 IOC Container 做 repository 层单例
Using IOC Container to make repository layer singleton
我公司的 MVC 解决方案使用 IOC 容器将 Caching/Repository 层注入控制器。这是非常昂贵的,因为目前我们每次创建控制器时都会生成新的 类(可能达到数千个对象,因为缓存层对象具有对回购层的引用 - 所有这些 类 得到创建)。我知道由于很多原因(参见 Why Singletons are Evil),Singleton 模式非常不受欢迎,但是是否有任何理由不将 caching/repo 层对象的 IOC 容器设置为单例?
谢谢。
首先。 .NET 可以在很短的时间内创建数百万个对象。使用 IoC 有点慢,但不是那么慢。 benchmark 测试了几个 IoC,它们在几秒钟内解决了 500 000 个对象。
My company's MVC Solution uses an IOC Container to inject the Caching/Repository layer into the controllers. This is incredibly expensive as currently we're generating new classes each time we create the controller (can amount to thousands of objects as the caching layer objects have refs to the repo layer - and all those classes get created).
你是说每次请求都会重新生成缓存?那你做错了什么。
缓存通常是单个实例(即由容器创建一次,然后每次解析缓存时返回相同的实例)。
存储库必须是每个请求,因为它需要数据库连接 and/or 事务。而您想保留那些短命的东西。
所以问题不在于 IoC,而在于您如何设计缓存、存储库和缓存对象之间的交互。
Sorry for not explaining clearly. We use dapper for the Repo layer so the Repo classes only create a connection to the Database when a method is called. The cache is using HTTPCache and Redis, but we have Cache Repo Classes that handle the logic (if in cache..) If each user call is creating thousands of duplicate objects that seems like it can tax the servers resources unnecessarily
我还没有和你在一起。恕我直言,缓存实体是持久层中的一个实现细节。
- 您的存储库是有范围的(每个 http 请求创建一个对象)或瞬态的(每次都有新对象)
- 您的缓存是 SingleInstance(在每个应用程序生命周期的对象上创建)
- 您的存储库包含对缓存的引用(构造函数注入)
- 当您的存储库无法在缓存中找到对象时,它会查询数据库
为什么缓存只能在存储库中使用(而不是作为持久性实现细节)?因为它降低了复杂性,因为存储库是系统中唯一 类 无需额外逻辑即可知道所有实体修改的存储库。
你把事情搞混了。 Singleton 设计模式与 DI 库使用的 Singleton 生活方式完全不同。
使用单例模式,您通常会在具体 class 上定义一个 public 静态只读字段,该字段包含 class 的唯一实例;此实例由 class 本身创建,整个应用程序都可以访问该只读字段。例如:
public static class CarEngine
{
public static readonly CarEngine Instance = new CarEngine();
// class methods
}
通过单例生活方式,您可以指示容器在该容器的生命周期内只创建一个实例并重复使用它。
Singleton 设计模式是一个问题,因为它迫使消费者对具体 class 产生硬依赖(违反依赖倒置原则)并且因为该具体 class 在内部控制创建后,在测试期间使用伪造的实现变得更加困难。除此之外,由于消费者没有需要 class 作为依赖项的构造函数,因此可以有效地使依赖项对阅读代码、创建测试的人以及作为可以进行对象图分析的 DI 库的工具隐藏为你。 The article 你指出的实际上 非常好 解释了为什么 Singleton 设计模式不好。
然而这篇文章并没有真正提到单例的生活方式,但是由于它讨论了单例设计模式如何隐藏依赖关系,这意味着应该注入依赖关系。并且由于您希望某些 classes 具有一个实例并通过构造函数注入它们,因此 Singleton 生活方式是解决此问题的实际方法。
Singleton 生活方式解决了这些问题,因为你从具体 class 中转移了创建单个实例的责任,这允许消费者依赖于抽象而不是在他们的构造函数中,这使得依赖可见并且代码更可测试。
所以让你的注册单例没有错。事实上,我认为您应该尽可能多地注册单例,因为这可以防止开发人员在实践依赖注入时通常面临的大量问题。通过使每个组件不可变和无状态,它们变得更容易推理,并且您可以防止自己不小心将运行时数据注入组件,即 bad practice. Another common pitfall with DI is Captive Dependencies,这意味着组件依赖于另一个组件,而组件的生命周期应该更短.如果你让所有的组件都是不可变的、无状态的和单例的,Captive Dependencies 的问题就会消失,因为单例组件可以安全地相互依赖。
当然,您的组件中始终需要运行时数据(例如请求数据、O/RM 上下文等),但这些可以在运行时通过注入提供程序或简单的 Func<DbContext>
到适配器实现中,将第三方工具从您的应用程序中抽象出来(如果您遵循 SOLID,这是一个很好的做法)。这个 Whosebug 对此进行了更详细的介绍。
我公司的 MVC 解决方案使用 IOC 容器将 Caching/Repository 层注入控制器。这是非常昂贵的,因为目前我们每次创建控制器时都会生成新的 类(可能达到数千个对象,因为缓存层对象具有对回购层的引用 - 所有这些 类 得到创建)。我知道由于很多原因(参见 Why Singletons are Evil),Singleton 模式非常不受欢迎,但是是否有任何理由不将 caching/repo 层对象的 IOC 容器设置为单例?
谢谢。
首先。 .NET 可以在很短的时间内创建数百万个对象。使用 IoC 有点慢,但不是那么慢。 benchmark 测试了几个 IoC,它们在几秒钟内解决了 500 000 个对象。
My company's MVC Solution uses an IOC Container to inject the Caching/Repository layer into the controllers. This is incredibly expensive as currently we're generating new classes each time we create the controller (can amount to thousands of objects as the caching layer objects have refs to the repo layer - and all those classes get created).
你是说每次请求都会重新生成缓存?那你做错了什么。
缓存通常是单个实例(即由容器创建一次,然后每次解析缓存时返回相同的实例)。
存储库必须是每个请求,因为它需要数据库连接 and/or 事务。而您想保留那些短命的东西。
所以问题不在于 IoC,而在于您如何设计缓存、存储库和缓存对象之间的交互。
Sorry for not explaining clearly. We use dapper for the Repo layer so the Repo classes only create a connection to the Database when a method is called. The cache is using HTTPCache and Redis, but we have Cache Repo Classes that handle the logic (if in cache..) If each user call is creating thousands of duplicate objects that seems like it can tax the servers resources unnecessarily
我还没有和你在一起。恕我直言,缓存实体是持久层中的一个实现细节。
- 您的存储库是有范围的(每个 http 请求创建一个对象)或瞬态的(每次都有新对象)
- 您的缓存是 SingleInstance(在每个应用程序生命周期的对象上创建)
- 您的存储库包含对缓存的引用(构造函数注入)
- 当您的存储库无法在缓存中找到对象时,它会查询数据库
为什么缓存只能在存储库中使用(而不是作为持久性实现细节)?因为它降低了复杂性,因为存储库是系统中唯一 类 无需额外逻辑即可知道所有实体修改的存储库。
你把事情搞混了。 Singleton 设计模式与 DI 库使用的 Singleton 生活方式完全不同。
使用单例模式,您通常会在具体 class 上定义一个 public 静态只读字段,该字段包含 class 的唯一实例;此实例由 class 本身创建,整个应用程序都可以访问该只读字段。例如:
public static class CarEngine
{
public static readonly CarEngine Instance = new CarEngine();
// class methods
}
通过单例生活方式,您可以指示容器在该容器的生命周期内只创建一个实例并重复使用它。
Singleton 设计模式是一个问题,因为它迫使消费者对具体 class 产生硬依赖(违反依赖倒置原则)并且因为该具体 class 在内部控制创建后,在测试期间使用伪造的实现变得更加困难。除此之外,由于消费者没有需要 class 作为依赖项的构造函数,因此可以有效地使依赖项对阅读代码、创建测试的人以及作为可以进行对象图分析的 DI 库的工具隐藏为你。 The article 你指出的实际上 非常好 解释了为什么 Singleton 设计模式不好。
然而这篇文章并没有真正提到单例的生活方式,但是由于它讨论了单例设计模式如何隐藏依赖关系,这意味着应该注入依赖关系。并且由于您希望某些 classes 具有一个实例并通过构造函数注入它们,因此 Singleton 生活方式是解决此问题的实际方法。
Singleton 生活方式解决了这些问题,因为你从具体 class 中转移了创建单个实例的责任,这允许消费者依赖于抽象而不是在他们的构造函数中,这使得依赖可见并且代码更可测试。
所以让你的注册单例没有错。事实上,我认为您应该尽可能多地注册单例,因为这可以防止开发人员在实践依赖注入时通常面临的大量问题。通过使每个组件不可变和无状态,它们变得更容易推理,并且您可以防止自己不小心将运行时数据注入组件,即 bad practice. Another common pitfall with DI is Captive Dependencies,这意味着组件依赖于另一个组件,而组件的生命周期应该更短.如果你让所有的组件都是不可变的、无状态的和单例的,Captive Dependencies 的问题就会消失,因为单例组件可以安全地相互依赖。
当然,您的组件中始终需要运行时数据(例如请求数据、O/RM 上下文等),但这些可以在运行时通过注入提供程序或简单的 Func<DbContext>
到适配器实现中,将第三方工具从您的应用程序中抽象出来(如果您遵循 SOLID,这是一个很好的做法)。这个 Whosebug