如何从外到内绑定装饰器与Ninject
本文关键字:Ninject 绑定 | 更新日期: 2023-09-27 18:14:06
我使用与WhenInjectedInto<>
的ninject绑定从内到外绑定装饰器。然而,从不同的入口点,我需要不同的功能,可能以不同的顺序运行,所以我想从外到内绑定装饰器链。Ninject可以做到吗?
。我想要实现这个目标:
Bind<IFooService>().To<SimpleService>().WhenInjectedInto<FeatureAFooServiceDecorator>();
Bind<IFooService>().To<FeatureAFooServiceDecorator>().WhenInjectedInto<FeatureBFooServiceDecorator>();
Bind<IFooService>().To<FeatureBFooServiceDecorator>().WhenInjectedInto<EntryPoint1>();
Bind<IFooService>().To<SimpleService>().WhenInjectedInto<FeatureBFooServiceDecorator>();
Bind<IFooService>().To<FeatureBFooServiceDecorator>().WhenInjectedInto<EntryPoint2>();
但这是不正确的,因为FeatureBFooServiceDecorator
不清楚它将被注入(FeatureAFooServiceDecorator
或SimpleService
)。
我想解决方案是让事情以另一种方式绑定,比如:
For<EntryPoint1>().Use<FeatureBFooServiceDecorator>().ThenUse<FeatureAFooServiceDecorator>().ThenUse<SimpleService>();
For<EntryPoint2>().Use<FeatureBFooServiceDecorator>().ThenUse<SimpleService>();
编辑:要手动完成此操作,我会这样做:
var entryPoint1 = new EntryPoint1(new FeatureBFooServiceDecorator(new FeatureAFooServiceDecorator(new SimpleService)));
var entryPoint2 = new EntryPoint2(new FeatureBFooServiceDecorator(new SimpleService));
(当然我会避免更新,因为这些类每个都有更多的依赖关系,其中一些是InRequestScope
或InNamedScope
)
注意:对于上面的例子,假设存在以下类:
public interface IFooService {/*...*/}
public class SimpleService : IFooService {/*...*/}
public class FeatureAFooServiceDecorator : IFooService
{
private readonly IFooService _innerFooService;
public FeatureAFooServiceDecorator(IFooService fooService) {
_innerFooService = fooService;
}
}
public class FeatureBFooServiceDecorator : IFooService {/*...same as above...*/}
public class EntryPoint1{
public EntryPoint1(IFooService fooService){/*...*/}
}
public class EntryPoint2{
public EntryPoint2(IFooService fooService){/*...*/}
}
我猜你想做的是
public class FeatureBFooService : IFooService
{
public FeatureBFooService(IFooService service1, IFooService service2)
{ ...}
}
var service = new FeatureBFooService(new FeatureAFooService(), new SimpleService());
(当然你不想自己做new
)。因此,您多次使用相同的接口,甚至对于相同的构造函数,但不仅希望不同的实例,而且希望将不同的类型(FeatureAFooService
, SimpleService
)注入FeatureBFooService
的构造函数。
IFooService
集合注入使用所有这些服务的类中是一回事,但是这个类本身又是一个IFooService
似乎有点奇怪。但话虽如此,我相信人们会做出自己的选择——有时也会犯错——因为这是最好的学习方式。当然,我的假设也可能是错误的,你所追求的是最好的可实现的解决方案。
方案一:.ToConstructor()
绑定
Bind<IFooService>().ToConstructor(ctorArg => new FeatureBFooService(ctorArg.Inject<FeatureAFooService>(), ctorArg.Inject<SimpleService>()));
对于每个构造函数参数,您可以定义应该注入的内容。这样你就不需要依赖于绑定,而且可以确定为给定的类型注入了什么。
方案二:[Named] binding
调整实现如下:
public const string FeatureAService = "FeatureA";
public const string SimpleService = "Simple";
public class FeatureBFooService : IFooService
{
public FeatureBFooService(
[Named(FeatureAService)]I FooService service1,
[Named(SimpleService] IFooService service2)
{ ...}
}
Bind<IFooService>().To<FeatureAService>().Named(FeatureAService);
Bind<IFooService>().To<SimpleService>().Named(SimpleService);
方案3:.ToProvider()
绑定+自定义绑定逻辑
你还可以做的是
Bind<IFooService>().ToProvider<FooServiceProvider>();
其中FooServiceProvider
将根据您的自定义逻辑决定要实例化的确切依赖项。它可以选择
IResolutionRoot.Get<FeatureAFooService>();
或者您仍然可以使用[Named]
特性:
IResolutionRoot.Get<IFooService>(FeatureAService);
例如,它可以看起来像(伪代码):
public class FooServiceProvider : Provider<IFooService>
{
protected override IFooService CreateInstance(IContext context)
{
Type returnType = DetermineImplementationType(context);
switch(returnType)
{
case typeof(FeatureBFooService):
return CreateFeatureBFooService(context);
break:
default:
throw new NotSupportedException(...);
}
}
private static Type DetermineImplementationType(IContext context)
{
// your custom logic here
}
private static IFooService CreateFeatureBFooService(IContext context)
{
var dependency1 = context.Kernel.Get<IFooService>(FeatureAFooService);
var dependency2 = context.Kernel.Get<IFooService>(SimpleService);
return context.Kernel.Get<IFooService>(
FeatureBFooService,
new ConstructorArgument("service1", dependency1),
new ConstructorArgument("service2", dependency2));
}
}
请注意,对于ConstructorArgument
,该值被注入到与名称(service1
, service2
)匹配的构造函数参数中,因此这是一个重构陷阱。另外请注意,如果需要保留上下文,也可以使用IContext.Kernel.ContextPreservingGet<>
。但是,这只能使用扩展名ninject.extensions.ContextPreservation.