使用 DI,每次使用接口时创建一个新实例
With DI, create a new instance every time the interface is used
我有类似下面的代码。
inyected class 有一个状态,所以我想创建一个干净的 new instance class 每次调用时实现 INumberWriter。有办法吗?
public class NumberWriter() : INumberWriter{
private int i = 1;
public void Write(){
ConsoleWrite(i++);
}
}
public class SomeClass(){
private INumberWriter writer;
public SomeClass(INumberWriter writer){
this.writer= writer;
}
public void AMethod(){
for(int i=0;i<5;i++){
writer.Write();// I want to see on console 11111 insted of 12345
}
}
}
当您在每次迭代中需要 一项新服务时,您肯定需要在每次迭代中创建 一项新服务。因此,在创建 SomeClass
-实例时提供一次依赖是没有意义的,但是在每次调用 AMethod
.
时提供依赖
您可以为您创建某种工厂 class 并让工厂在每次迭代时创建一个新实例:
public class WriterFactory
{
public INumberWriter => new NumberWriter();
}
public class SomeClass
{
private WriterFactory factory;
public SomeClass(WriterFactory factory){
this.factory = factory;
}
public void AMethod()
{
for(int i=0;i<5;i++)
{
var writer = this.factory().CreateNewWriter();
writer.Write();
}
}
}
现在,您无需为您的 class 提供编写器,而是为 class 提供 工厂,后者本身会创建编写器。
这不是您的 DI 容器可以(或者可以说应该)为您解决的问题。将依赖项注入消费者是 DI 容器的工作。之后,消费者将依赖项存储在私有字段中,这意味着 - 只要消费者还活着 - 它就会重用相同的依赖项。
幸运的是,您正在为接口编程,这意味着您可以使用特殊的 Proxy implementation that can be placed inside your application's Composition Root 来解决这个问题。例如:
public class AlwaysNewNumberWriter : INumberWriter
{
public void Write() => new NumberWriter().Write();
}
您现在可以按如下方式注册 AlwaysNewNumberWriter
,而不是在容器中注册 NumberWriter
:
services.AddSingleton<INumberWriter, AlwaysNewNumberWriter>();
但是,此解决方案可能过于简单,因为 NumberWriter
可能有其自身的依赖项,需要从 DI 容器中解析。在这种情况下,您需要一个稍微复杂的解决方案:
public class DispatchingNumberWriter : INumberWriter
where TNumberWriter : INumberWriter
{
private readonly Func<INumberWriter> factory;
public DispatchingNumberWriter(Func<INumberWriter> factory)
{
this.factory = factory;
}
public void Write() => this.factory.Invoke().Write();
}
这个 DispatchingNumberWriter
接受一个 Func<INumberWriter>
工厂委托,它会在每次调用其 Write
方法时调用它。这允许您注入此 DispatchingNumberWriter
.
而不是将 NumberWriter
实现注入消费者
以下代码演示了如何将所有内容连接在一起:
services.AddTransient<NumberWriter>();
services.AddScoped<INumberWriter>(c =>
new DispatchingNumberWriter(
() => c.GetRequiredService<NumberWriter>()));
请注意最后一个片段的以下内容:
DispatchingNumberWriter
随附一个委托,该委托会回调 DI 容器以解析新的 NumberWriter
。这允许 NumberWriter
由 DI 容器创建,它可能使用 Auto-Wiring 构造类型,即自动检测和注入其依赖项。
- 因为
NumberWriter
是由DI Container创建的,所以注册为Transient
;否则实例可能仍会被重用。
DispatchingNumberWriter
注册为 Scoped,这是此 class 最安全的生活方式,因为:
- 将
DispatchingNumberWriter
注册为 Singleton
将导致它的 c
参数和对其 GetRequiredService
方法的调用到容器的全局范围内的 运行。如果 NumberWriter
或其瞬态依赖项之一开始实现 IDisposable
(这可能导致内存泄漏),或者 NumberWriter
与 [= 具有直接或间接依赖项,这可能会导致问题35=] 生活方式(这会使这些依赖性 Singletons
)。
- 将
DispatchingNumberWriter
注册为 Transient
将产生预期的效果,除非它(意外地)直接或间接注入到 Singleton
消费者中。因为在那种情况下,您将获得与上一点中描述的相同的行为,其中 DispatchingNumberWriter
注册为 Singleton
。尽管将 Scoped
DispatchingNumberWriter
注入 Singleton
消费者时问题没有什么不同,但 MS.DI 阻止将 Scoped
依赖项注入 Singleton
消费者,因为它是known pitfall来预防的。
我有类似下面的代码。
inyected class 有一个状态,所以我想创建一个干净的 new instance class 每次调用时实现 INumberWriter。有办法吗?
public class NumberWriter() : INumberWriter{
private int i = 1;
public void Write(){
ConsoleWrite(i++);
}
}
public class SomeClass(){
private INumberWriter writer;
public SomeClass(INumberWriter writer){
this.writer= writer;
}
public void AMethod(){
for(int i=0;i<5;i++){
writer.Write();// I want to see on console 11111 insted of 12345
}
}
}
当您在每次迭代中需要 一项新服务时,您肯定需要在每次迭代中创建 一项新服务。因此,在创建 SomeClass
-实例时提供一次依赖是没有意义的,但是在每次调用 AMethod
.
您可以为您创建某种工厂 class 并让工厂在每次迭代时创建一个新实例:
public class WriterFactory
{
public INumberWriter => new NumberWriter();
}
public class SomeClass
{
private WriterFactory factory;
public SomeClass(WriterFactory factory){
this.factory = factory;
}
public void AMethod()
{
for(int i=0;i<5;i++)
{
var writer = this.factory().CreateNewWriter();
writer.Write();
}
}
}
现在,您无需为您的 class 提供编写器,而是为 class 提供 工厂,后者本身会创建编写器。
这不是您的 DI 容器可以(或者可以说应该)为您解决的问题。将依赖项注入消费者是 DI 容器的工作。之后,消费者将依赖项存储在私有字段中,这意味着 - 只要消费者还活着 - 它就会重用相同的依赖项。
幸运的是,您正在为接口编程,这意味着您可以使用特殊的 Proxy implementation that can be placed inside your application's Composition Root 来解决这个问题。例如:
public class AlwaysNewNumberWriter : INumberWriter
{
public void Write() => new NumberWriter().Write();
}
您现在可以按如下方式注册 AlwaysNewNumberWriter
,而不是在容器中注册 NumberWriter
:
services.AddSingleton<INumberWriter, AlwaysNewNumberWriter>();
但是,此解决方案可能过于简单,因为 NumberWriter
可能有其自身的依赖项,需要从 DI 容器中解析。在这种情况下,您需要一个稍微复杂的解决方案:
public class DispatchingNumberWriter : INumberWriter
where TNumberWriter : INumberWriter
{
private readonly Func<INumberWriter> factory;
public DispatchingNumberWriter(Func<INumberWriter> factory)
{
this.factory = factory;
}
public void Write() => this.factory.Invoke().Write();
}
这个 DispatchingNumberWriter
接受一个 Func<INumberWriter>
工厂委托,它会在每次调用其 Write
方法时调用它。这允许您注入此 DispatchingNumberWriter
.
NumberWriter
实现注入消费者
以下代码演示了如何将所有内容连接在一起:
services.AddTransient<NumberWriter>();
services.AddScoped<INumberWriter>(c =>
new DispatchingNumberWriter(
() => c.GetRequiredService<NumberWriter>()));
请注意最后一个片段的以下内容:
DispatchingNumberWriter
随附一个委托,该委托会回调 DI 容器以解析新的NumberWriter
。这允许NumberWriter
由 DI 容器创建,它可能使用 Auto-Wiring 构造类型,即自动检测和注入其依赖项。- 因为
NumberWriter
是由DI Container创建的,所以注册为Transient
;否则实例可能仍会被重用。 DispatchingNumberWriter
注册为 Scoped,这是此 class 最安全的生活方式,因为:- 将
DispatchingNumberWriter
注册为Singleton
将导致它的c
参数和对其GetRequiredService
方法的调用到容器的全局范围内的 运行。如果NumberWriter
或其瞬态依赖项之一开始实现IDisposable
(这可能导致内存泄漏),或者NumberWriter
与 [= 具有直接或间接依赖项,这可能会导致问题35=] 生活方式(这会使这些依赖性Singletons
)。 - 将
DispatchingNumberWriter
注册为Transient
将产生预期的效果,除非它(意外地)直接或间接注入到Singleton
消费者中。因为在那种情况下,您将获得与上一点中描述的相同的行为,其中DispatchingNumberWriter
注册为Singleton
。尽管将Scoped
DispatchingNumberWriter
注入Singleton
消费者时问题没有什么不同,但 MS.DI 阻止将Scoped
依赖项注入Singleton
消费者,因为它是known pitfall来预防的。
- 将