我将 ninject 绑定(bind)与 WhenInjectedInto<>
结合使用从内到外绑定(bind)装饰器。但是,从不同的入口点我需要不同的功能,可能以不同的顺序运行,所以我想从外到内绑定(bind)装饰器链。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
不清楚它将被注入(inject)什么( FeatureAFooServiceDecorator
或 SimpleService
)。
我想解决方案是将事物反过来绑定(bind),例如:
伪代码
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
)。所以你多次使用同一个接口(interface),即使是同一个构造函数,但不仅要不同的实例,还要将不同的类型( FeatureAFooService
, SimpleService
)注入(inject)到 FeatureBFooService
的构造函数中.
我可以想到两种方法来实现这一点。
但老实说,我应该警告你这看起来很复杂。通常这意味着设计不理想,你最好考虑如何以不同的方式解决问题。毕竟,当实现共享同一个接口(interface)时,它们不应该做同样的事情吗?注入(inject) IFooService
的集合是一回事进入一个使用所有这些服务的类,但该类本身又是一个IFooService
似乎有点奇怪。
但足够说了,我相信人们会做出自己的选择——有时会犯错误——因为这是最好的学习方式。当然,我的假设也可能是错误的,而您所追求的是可实现的最佳解决方案。
解决方案 1:.ToConstructor()
绑定(bind)
Bind<IFooService>().ToConstructor(ctorArg => new FeatureBFooService(ctorArg.Inject<FeatureAFooService>(), ctorArg.Inject<SimpleService>()));
对于每个构造函数参数,您都可以定义应该注入(inject)的内容。这样您就不会依赖绑定(bind),并且可以确定为给定类型注入(inject)的内容。
解决方案 2:[命名] 绑定(bind)
按如下方式调整实现:
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)+自定义绑定(bind)逻辑
你也可以做,就是做
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
,该值被注入(inject)到与名称匹配的构造函数参数中( service1
, service2
),因此这是一个重构陷阱。
另请注意,您还可以使用 IContext.Kernel.ContextPreservingGet<>
如果您需要保留上下文。但是,这仅适用于扩展 ninject.extensions.ContextPreservation。
关于c# - 如何从外到内用 Ninject 绑定(bind)装饰器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24894652/