如何在Ninject中修饰命名绑定

时间:2016-05-17 20:25:01

标签: c# polymorphism ninject decorator ioc-container

我有一个有多个实现的接口。这些实现是通过命名绑定设置的:

Bind<IService>().To<FirstService>().Named("First");
Bind<IService>().To<SecondService>().Named("Second");
Bind<IService>().To<ThirdService>().Named("Third");

但是,每个服务都需要进行装饰:

Bind<IService>().To<FirstService>().WhenInjectedInto<FirstDecorator>();
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>();
Bind<IService>().To<SecondDecorator>();

我知道有WhenParentNamedWhenAnyAncestorNamed内容绑定,但它们似乎与WhenInjectedInto互斥。如何设置Ninject以便它既可以装饰也可以命名绑定?我有一个Provider方案设置确实有效,但我想知道是否有办法在Ninject中本地执行此操作,而不使用自定义提供程序。

Bind<IService>().ToProvider(new ServiceProvider("First")).Named("First");
Bind<IService>().ToProvider(new ServiceProvider("Second")).Named("Second");
Bind<IService>().ToProvider(new ServiceProvider("Third")).Named("Third");
class ServiceProvider : Provider<IService>
{
    private readonly string name;

    public ServiceProvider(string name)
    {
        this.name = name;
    }

    protected override CreateInstance(IContext context)
    {
        var service = GetService(name);

        var otherDependency = context.Kernel.Get<OtherDependency>();
        service = new FirstDecorator(service, otherDependency);

        service = new SecondDecorator(service);

        return service;
    }

    private IService GetService(string name, IContext context)
    {
        switch(name)
        {
            case "First": return context.Kernel.Get<FirstService>();
            case "Second": return context.Kernel.Get<SecondService>();
            case "Third": return context.Kernel.Get<ThirdService>();
            default: throw new ArugmentException($"No binding for {name}");
        }
    }
}

我查看了this,但由于它没有处理命名绑定,因此它似乎不适用。

理想情况下,它会是这样的:

Bind<IService>().To<FirstService>().WhenInjectedInto<FirstDecorator>().Named("First");
Bind<IService>().To<SecondService>().WhenInjectedInto<FirstDecorator>().Named("Second");
Bind<IService>().To<ThirdService>().WhenInjectedInto<FirstDecorator>().Named("Third");
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>();
Bind<IService>().To<SecondDecorator>();

更新:以下是我希望调用其中一种装饰服务的方法:

kernel.Get<IService>("First");

[Inject, Named("First")]
public IService Service {get; set;}

最后一个是自动集成测试。

更新2 :我已经尝试了这个,它不起作用(抱怨重复绑定,大概是因为它没有尊重具体实现绑定上的名称)

Bind<IService>().To<FirstService>().WhenInjectedInto<FirstDecorator>().Named("First");
Bind<IService>().To<SecondService>().WhenInjectedInto<FirstDecorator>().Named("Second");
Bind<IService>().To<ThirdService>().WhenInjectedInto<FirstDecorator>().Named("Third");
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("First");
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("Second");
Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("Third");
Bind<IService>().To<SecondDecorator>().Named("First");
Bind<IService>().To<SecondDecorator>().Named("Second");
Bind<IService>().To<SecondDecorator>().Named("Third");

更新3:执行以下ALMOST工作:

Bind<IService>().To<FirstService>().WhenAnyAncestorNamed("First");
Bind<IService>().To<SecondService>().WhenAnyAncestorNamed("Second");
Bind<IService>().To<ThirdService>().WhenAnyAncestorNamed("Third");

Bind<IService>().To<FirstDecorator>().WhenInjectedInto<SecondDecorator>().Named("First");

Bind(typeof(IService)).To(typeof(SecondDecorator)).Named("First");
Bind(typeof(IService)).To(typeof(SecondDecorator)).Named("Second");
Bind(typeof(IService)).To(typeof(SecondDecorator)).Named("Third");

问题在于,当它尝试构建SecondDecorator时,有两个匹配的绑定:一个到具体实现(如果命名为“First”,然后是FirstService),一个到{ {1}}。

1 个答案:

答案 0 :(得分:0)

我最终手工制作了自己的装饰方法:

public override void Load()
{
    var decorators = new[]
    {
        typeof(FirstDecorator),
        typeof(SecondDecorator)
    };

    Decorate<IService, FirstService>("First", decorators);
    Decorate<IService, SecondService>("Second", decorators);
    Decorate<IService, ThirdService>("Third", decorators);
}

private void Decorate<S, T>(string name, params Type[] decorators)
    where T : S
{
    var allImplementations = new[] { typeof(T) }.Union(decorators);

    foreach (var implementation in allImplementations)
    {
        Bind<S>().To(implementation).When(r => Need(implementation, r, name, allImplementations));
    }

    Bind<S>().To(allImplementations.Last()).Named(name);
}

private bool Need(Type implementation, IRequest request, string name, IEnumerable<Type> implementations)
{
    var implementationsList = implementations.ToList();
    var depth = implementationsList.Count - implementationsList.IndexOf(implementation);

    return request.Depth == depth && request.ActiveBindings.Any(b => b.Metadata.Name == name);
}

我将查看是否可以将其重构为When - 类型子句并作为拉取请求提交给Ninject