我如何注册MyClass与DI,但有一个选项,以传递someRandomInput每次我需要得到一个实例?
假设只需要指定一次someRandomInput
,那么您可以在服务注册的implementationFactory:
中这样做:
IConfiguration cfg = ...
//
services
.AddSingleton<MyClass>( implementationFactory: ( IServiceProvider sp ) =>
new MyClass(
srs : sp.GetRequiredService<SomeRequiredService>(),
someRandomInput: cfg.GetString("Foobars")
)
);
同样适用于AddTransient
和AddScoped
-问题是它们都将使用您在工厂中传递到someRandomInput
的任何值-尽管您的工厂方法本身也可以使用IServiceProvider
来获取another服务作为这些值的来源.
更普遍的是,这里有两种方法:
选项1:定义表示服务的数据依赖关系的接口
public interface IFoobarsServiceRequisite
{
String GetNextStringValue();
}
public class FoobarsService
{
public FoobarsService( SomeRequiredService srs, IFoobarsServiceRequisite requisiteData )
{
}
}
...然后您将注册那个Requisite
服务接口的一些实现.
Option 2: Not-quite-as-bad-as AbstractSingletonProxyFactoryBean
(In case anyone missed the reference)
services
.AddSingleton<SomeRequiredService>()
.AddSingleton<ISomeRandomStringSource,DefaultSomeRandomStringSource>()
.AddSingleton<IFoobarsServiceFactory,DefaultFoobarsServiceFactory>()
.AddTransient<FoobarsService>( sp => sp.GetRequiredService<IFoobarsServiceFactory>().CreateFoobar() );
//
public interface IFoobarsServiceFactory
{
FoobarsService CreateFoobar();
}
public class DefaultFoobarsServiceFactory : IFoobarsServiceFactory
{
private readonly SomeRequiredService srs;
private readonly ISomeRandomStringSource src;
public DefaultFoobarsServiceFactory(
SomeRequiredService srs,
ISomeRandomStringSource src
)
{
this.srs = srs;
this.src = src;
}
public FoobarsService CreateFoobar()
{
return new FoobarsService( this.srs, this.src.GetNextValue() );
}
}
public class FoobarsService
{
public FoobarsService( SomeRequiredService srs, String someRandomValue )
{
}
}
所有的工厂都是这样.
我感到惊讶的是,MEDI仍然缺乏一个标准化的IServiceFactory
接口来处理尴尬的边缘情况,这些情况需要比简单的sp =>
lambda更多的工厂逻辑,但在应用程序代码中没有need显式调用它们的.CreateService()
方法-特别是EF已经有了自己的IDbContextFactory
类型.
更新:选项3:工厂服务与呼叫者提供的参数
在澄清了 comments 中的事情后,我认为Op的目标是这样的:
FoobarServiceFactory
(或其interface
)是一个普通的Singleton服务,它有FoobarService
个依赖项,但也允许/要求FoobarServiceFactory
的使用者提供他们自己的参数,说明他们何时想要获得FoobarService
的实例,如下所示:
services.AddSingleton<IFoobarServiceFactory,FoobarServiceFactory>()
//
public interface IFoobarServiceFactory // Note that this interface is entirely optional: you could just register `FoobarServiceFactory` as a service.
{
FoobarsService CreateFoobar( String callerProvidedData );
}
public class FoobarServiceFactory: IFoobarServiceFactory
{
private readonly SomeRequiredService srs;
public DefaultFoobarsServiceFactory( SomeRequiredService srs )
{
this.srs = srs;
}
public FoobarsService CreateFoobar( String callerProvidedData )
{
return new FoobarService( this.srs, callerProvidedData );
}
}
public class FoobarService
{
public FoobarService( SomeRequiredService srs, String someRandomValue )
{
// etc
}
}
用法如下:
public class SomeConsumer
{
private readonly IFoobarServiceFactory fbFactory;
public SomeConsumer( IFoobarServiceFactory fbFactory )
{
this.fbFactory = fbFactory;
}
public void DoSomething()
{
String someRandomString = Math.Random() > 0.5 ? "abc" : "0xdeadbeef";
FoobarService svc = this.fbFactory.Create( someRandomString );
// do stuff with `svc`
}
}
IFoobarServiceFactory
的替代实现可以使用IServiceProvider
,如果不可能为SomeRequiredService
保留长生命周期 (或未绑定的生命周期 )(并且IMO,这是需要IServiceProvider
作为实际依赖的唯一可接受的原因(除了依赖关系图中的中断循环,ofc):
public class AltFoobarServiceFactory : IFoobarServiceFactory
{
private readonly IServiceProvider sp;
public AltFoobarServiceFactory( IServiceProvider sp )
{
this.sp = sp;
}
public FoobarsService CreateFoobar( String callerProvidedData )
{
// `ActivatorUtilities` is useful! https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.activatorutilities?view=dotnet-plat-ext-8.0
return ActivatorUtilities.CreateInstance<FoobarsService>( sp, callerProvidedData );
}
}