Связывание Ninject для реализации диспетчера интерфейса

У меня есть интерфейс:

public interface IService
{
    void DoStuff(int parm1, string parm2, Guid gimmeABreakItsAnExampleK);
}

Я хотел бы настроить привязки Ninject (v3), чтобы у меня мог быть метод shuffle "dispatcher", вызывающий несколько экземпляров IService, например:

public sealed class DispatcherService : IService
{
    private IEnumerable<IService> _children;

    public DispatcherService(IEnumerable<IService> children)
    {
        this._children = children.ToList();
    }

    public void DoStuff(int parm1, string parm2, Guid gimmeABreakItsAnExampleK)
    {
        foreach(var child in this._children)
        {
            child.DoStuff(parm1, parm2, gimmeABreakItsAnExampleK);
        }
    }
}

Однако мои привязки, которые выглядят так, завершают выдачу исключения во время выполнения, указывающее циклическую зависимость:

this.Bind<IService>().To<DispatcherService>();

this.Bind<IService>().To<SomeOtherService>()
    .WhenInjectedExactlyInto<DispatcherService>();
this.Bind<IService>().To<YetAnotherService>()
    .WhenInjectedExactlyInto<DispatcherService>();

Возможно ли это? Если да, то что я делаю неправильно? Может ли ниндзя избежать этой циклической гибели зависимости?

Ответ 1

Если ваш диспетчер является единственным IService, который будет принимать список IServices в качестве параметра, это работает (я тестировал):

kernel.Bind<IService>().To<DispatcherService>().When(x => x.IsUnique);
this.Bind<IService>().To<SomeOtherService>()
    .WhenInjectedExactlyInto<DispatcherService>();
this.Bind<IService>().To<YetAnotherService>()
    .WhenInjectedExactlyInto<DispatcherService>();

Причина, по которой это предложение When работает для этого случая, заключается в том, что поле IsUnique IRequest устанавливается в true, когда ваш конструктор вызывает один экземпляр службы. Поскольку ваш DispatcherService вызывает IEnumerable, при DispatcherService значение false. Это предотвращает появление циклической зависимости.

Действительно, любой правильный способ сказать ядру не пытаться внедрить DispatcherService в себя будет работать (это просто потенциально полезный пример).

Изменить: более явный способ просто короткого замыкания вашей циклической зависимости выглядит следующим образом:

kernel.Bind<IService>().To<DispatcherService>().When(
   request => request.Target.Member.DeclaringType != typeof (DispatcherService));

Ответ 2

Я должен признать, что я не знаком с API Ninject, но я думаю, что это сделало бы трюк:

kernel.Bind<IService>().To<DispatcherService>();   

kernel.Bind<IEnumerable<IService>>().ToMethod(() => new IService[]
{
    kernel.Get<SomeOtherService>(),
    kernel.Get<YetAnotherService>(),
});

Ответ 3

Почему бы не избавиться от IService в DispatcherService и назвать его IDispatcherService и заставить вызываемые службы (приемники) реализовать IService?

Ответ 4

Вы можете отделить два подмножества (либо Диспетчер, либо ресиверы) от пути, сделав одно из них Именованным Связыванием, а затем используйте это имя как способ подачи одного к другому (либо через NamedAttribute или в вашей проводке)