c# - 如何从外到内用 Ninject 绑定(bind)装饰器?

标签 c# dependency-injection ninject decorator

我将 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)什么( FeatureAFooServiceDecoratorSimpleService )。

我想解决方案是将事物反过来绑定(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));

(当然我会避免更新东西,因为这些类每个都有更多的依赖关系,其中一些是 InRequestScopeInNamedScope )

注意:对于上面的例子,假设有这些类:

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),即使是同一个构造函数,但不仅要不同的实例,还要将不同的类型( FeatureAFooServiceSimpleService )注入(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)到与名称匹配的构造函数参数中( service1service2 ),因此这是一个重构陷阱。 另请注意,您还可以使用 IContext.Kernel.ContextPreservingGet<>如果您需要保留上下文。但是,这仅适用于扩展 ninject.extensions.ContextPreservation。

关于c# - 如何从外到内用 Ninject 绑定(bind)装饰器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24894652/

相关文章:

c# - 为什么 Object.GetType() 是方法而不是属性?

.net-core - .NET Core 默认依赖注入(inject)与 CaSTLe DynamicProxy

c# - Ninject 2 : Is there an equivalent to 1. 5的InjectProperties在哪里?

c# - 为什么设置字段比获取字段慢很多倍?

c# - FileSystemWatcher InternalBufferSize 的理想大小是多少?

c# - 如何处理从 VBA 中的 C# 方法返回的字符串数组

node.js - InjectRepository 如何在 NestJS 内部工作?

javascript - 将 $http 拦截器创建为独立模块时 Angular 中的依赖项错误

c# - 如何使用 lambda 指定构造函数参数?

c# - Ninject WCF Bootstrap 注册多个服务