ContextDependent 实例作为简单注入器中的单例
ContextDependent instance as Singleton in Simple Injector
我有一个关于 SimpleInjector
库的 ContextDependentExtensions
的问题。
我们有以下扩展来添加在某些上下文中将元素添加到注入器的可能性:ContextDependentExtensions
示例:
var container = new Container();
container.RegisterWithContext(logger => new SimpleLogger("Logger Constructor Parameter"));
这是一条兴趣线:
Should always be transient!
那么,这是什么意思?我们可以在这里使用 Lifestyle.Singleton
或 Lifestyle.Scoped
生活方式吗?
有人可以为我解释一下吗?
提前致谢。
使用 RegisterWithContext
扩展方法,您可以注册一个谓词,允许您使用有关消费组件的信息构建一个实例。此信息由扩展方法提供给谓词。
因为这意味着您可以为每个消费类型构建一个完全不同的实例,使注册成为瞬时的,可能会导致非常奇怪的行为。想象一下,例如注册一个 ILogger
抽象,你在其中创建一个 Logger<T>
实现,其中 T
是消费组件的类型:
container.RegisterWithContext(c =>
(ILogger).container.GetInstance(
typeof(Logger<>).MakeGenericType(c.ImplementationType)));
如果您将此注册设为单例,这会导致问题,即使消费组件是单例,因为相同的实例将被注入每个消费者;而每个消费者都需要自己的实例,因为他们需要自己的封闭通用版本,即:Logger<Consumer1>
、Logger<Consumer2>
、Logger<Consumer3>
等。相反,每个消费者都会获得相同的实例;为第一个解析的消费者创建的实例。这显然很糟糕。
同样的问题在使用Scoped实例时也会存在;您将在作用域的持续时间内获得相同的实例,这通常不是您想要的;该实例应该依赖于上下文。
这是随 Simple Injector v2 文档提供的 RegisterWithContext
扩展方法的严重限制。由于这限制太多,Simple Injector v3 现在包含一个内置的 RegisterConditional
方法来替换 RegisterWithContext
扩展方法。 v3 文档不再提及 RegisterWithContext
,我们建议改用 RegisterWithContext
。
文档 describes 如何使用 RegisterConditional
并显示了以下示例:
container.RegisterConditional(
typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
由于此注册的谓词 returns true
,注册并不是真正有条件的,而只是上下文相关的。
使用此代码,您可以 return 特定于消费组件的 Logger<T>
,但仍然确保每个关闭的 Logger<T>
类型最多有一个实例;因此是单身人士。
新 RegisterConditional
和旧 RegisterWithContext
之间的主要区别是您不能提供工厂委托来创建实例。使用 RegisterConditional
,Simple Injector 控制实例的创建(而不是您的委托),这允许类型的创建完全合并到管道中,并允许 Simple Injector 验证和诊断已注册的组件及其依赖项.
我有一个关于 SimpleInjector
库的 ContextDependentExtensions
的问题。
我们有以下扩展来添加在某些上下文中将元素添加到注入器的可能性:ContextDependentExtensions
示例:
var container = new Container();
container.RegisterWithContext(logger => new SimpleLogger("Logger Constructor Parameter"));
这是一条兴趣线: Should always be transient!
那么,这是什么意思?我们可以在这里使用 Lifestyle.Singleton
或 Lifestyle.Scoped
生活方式吗?
有人可以为我解释一下吗? 提前致谢。
使用 RegisterWithContext
扩展方法,您可以注册一个谓词,允许您使用有关消费组件的信息构建一个实例。此信息由扩展方法提供给谓词。
因为这意味着您可以为每个消费类型构建一个完全不同的实例,使注册成为瞬时的,可能会导致非常奇怪的行为。想象一下,例如注册一个 ILogger
抽象,你在其中创建一个 Logger<T>
实现,其中 T
是消费组件的类型:
container.RegisterWithContext(c =>
(ILogger).container.GetInstance(
typeof(Logger<>).MakeGenericType(c.ImplementationType)));
如果您将此注册设为单例,这会导致问题,即使消费组件是单例,因为相同的实例将被注入每个消费者;而每个消费者都需要自己的实例,因为他们需要自己的封闭通用版本,即:Logger<Consumer1>
、Logger<Consumer2>
、Logger<Consumer3>
等。相反,每个消费者都会获得相同的实例;为第一个解析的消费者创建的实例。这显然很糟糕。
同样的问题在使用Scoped实例时也会存在;您将在作用域的持续时间内获得相同的实例,这通常不是您想要的;该实例应该依赖于上下文。
这是随 Simple Injector v2 文档提供的 RegisterWithContext
扩展方法的严重限制。由于这限制太多,Simple Injector v3 现在包含一个内置的 RegisterConditional
方法来替换 RegisterWithContext
扩展方法。 v3 文档不再提及 RegisterWithContext
,我们建议改用 RegisterWithContext
。
文档 describes 如何使用 RegisterConditional
并显示了以下示例:
container.RegisterConditional(
typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
由于此注册的谓词 returns true
,注册并不是真正有条件的,而只是上下文相关的。
使用此代码,您可以 return 特定于消费组件的 Logger<T>
,但仍然确保每个关闭的 Logger<T>
类型最多有一个实例;因此是单身人士。
新 RegisterConditional
和旧 RegisterWithContext
之间的主要区别是您不能提供工厂委托来创建实例。使用 RegisterConditional
,Simple Injector 控制实例的创建(而不是您的委托),这允许类型的创建完全合并到管道中,并允许 Simple Injector 验证和诊断已注册的组件及其依赖项.