c# - tipos - que es una interfaz en programacion
Enlaces Ninject para una implementación del despachador de una interfaz (4)
Tengo una interfaz:
public interface IService
{
void DoStuff(int parm1, string parm2, Guid gimmeABreakItsAnExampleK);
}
Me gustaría configurar enlaces de Ninject (v3) para que pueda hacer que un método aleatorio "dispatcher" llame a varias instancias de IService
, de esta forma:
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);
}
}
}
Sin embargo, mis enlaces, que se ven así, terminan arrojando una excepción en el tiempo de ejecución que indica una dependencia cíclica:
this.Bind<IService>().To<DispatcherService>();
this.Bind<IService>().To<SomeOtherService>()
.WhenInjectedExactlyInto<DispatcherService>();
this.Bind<IService>().To<YetAnotherService>()
.WhenInjectedExactlyInto<DispatcherService>();
es posible? Si es así, ¿qué estoy haciendo mal? ¿Pueden los ninja escapar de esta condena a la dependencia cíclica?
¿Por qué no deshacerse de IService en el DispatcherService y llamarlo IDispatcherService y hacer que los servicios llamados (receptores) implementen IService?
Debo admitir que no estoy tan familiarizado con la API de Ninject, pero creo que esto sería el truco:
kernel.Bind<IService>().To<DispatcherService>();
kernel.Bind<IEnumerable<IService>>().ToMethod(() => new IService[]
{
kernel.Get<SomeOtherService>(),
kernel.Get<YetAnotherService>(),
});
Puede segregar los dos subconjuntos (ya sea Dispatcher o los receptores) haciendo que uno de ellos sea un NamedAttribute
nombre, y luego usar el nombre como una forma de alimentar el uno al otro (ya sea a través de una NamedAttribute
o en su cableado)
Si su despachador es el único IService que tomará una lista de IServices como parámetro, esto funciona (lo he probado):
kernel.Bind<IService>().To<DispatcherService>().When(x => x.IsUnique);
this.Bind<IService>().To<SomeOtherService>()
.WhenInjectedExactlyInto<DispatcherService>();
this.Bind<IService>().To<YetAnotherService>()
.WhenInjectedExactlyInto<DispatcherService>();
La razón por la que la cláusula When
funciona para este caso es porque el campo IsUnique
de IRequest
se establece en true
cuando el constructor solicita una instancia única de un servicio. Como su DispatcherService
llama a un IEnumerable
, el valor es false
cuando se activa el DispatcherService
. Eso evita que la dependencia circular ocurra.
En realidad, cualquier forma correcta de decirle al kernel que no intente inyectar el DispatcherService en sí mismo funcionaría (este es solo un ejemplo potencialmente útil).
Editar: La forma más explícita de simplemente cortocircuitar tu dependencia circular parece ser esta:
kernel.Bind<IService>().To<DispatcherService>().When(
request => request.Target.Member.DeclaringType != typeof (DispatcherService));