2010-08-18 11 views
9

Estoy tratando de encontrar la manera correcta de enlazar algo como esto con ninject.Dependencia cíclica con ninject

interface IMainService 
{ 
    void DoStuff(); 
} 

interface IOtherService 
{ 
    void DoSomeMagic(); 
} 

abstract class BaseClass 
{ 
    //many stuff here 
} 

class MainClass : BaseClass, IMainService 
{ 
    public MainClass(IOtherService s) 
    { 
    } 

    public void DoStuff() 
    { 
     throw new NotImplementedException(); 
    } 

    //do many other things 
} 

class OtherClass : IOtherService 
{ 
    public OtherClass(IMainService s) 
    { 
    } 

    public void DoSomeMagic() 
    { 
     throw new NotImplementedException(); 
    } 
} 

class BaseModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<MainClass>().To<MainClass>(); 
     Bind<IMainService>().To<MainClass>(); 
     Bind<IOtherService>().To<OtherClass>(); 
    } 
} 

static class Program 
{ 
    static void Main() 
    { 
     var kernel = new StandardKernel(new BaseModule()); 
     var main = kernel.Get<MainClass>(); 
    } 
} 

Me da una excepción:

Error activating IOtherService using binding from IOtherService to OtherClass 
A cyclical dependency was detected between the constructors of two services. 

Activation path: 
    4) Injection of dependency IOtherService into parameter s of constructor of type MainClass 
    3) Injection of dependency IMainService into parameter s of constructor of type OtherClass 
    2) Injection of dependency IOtherService into parameter s of constructor of type MainClass 
    1) Request for MainClass 

Suggestions: 
    1) Ensure that you have not declared a dependency for IOtherService on any implementations of the service. 
    2) Consider combining the services into a single one to remove the cycle. 
    3) Use property injection instead of constructor injection, and implement IInitializable if you need initialization logic to be run after property values have been injected. 

No sé cómo escribir BaseModule. Necesito solo una instancia de MainClass y una instancia de OtherClass (como singletons).

he intentado cosas por el estilo:

Bind<MainClass>().To<MainClass>().InSingletonScope(); 
Bind<IMainService>().To<MainClass>().InRequestScope(); 
Bind<IOtherService>().To<OtherClass>().InSingletonScope(); 

pero con el mismo error.

¿Y cómo escribir el enlace para usar solo una instancia para las interfaces MainClass y IMainService?

Gracias por responder.

Respuesta

15

Como dice el mensaje de error, tiene una dependencia cíclica entre MainClass y OtherClass ya que no puede crear una sin una instancia de la otra. Idealmente, debe reestructurar su jerarquía de clases para eliminar este requisito.

Si no puede, la solución es usar la inyección de propiedades para una (o ambas) clases, p.

public interface IMainService 
{ 
    void DoStuff(); 
    IOtherService OtherService { set; } 
} 

public class MainClass 
{ 
    public IOtherService OtherService { get; set; } 
    public void DoStuff() { ... } 
} 

public class OtherService 
{ 
    public OtherService(IMainService main) 
    { 
     main.OtherService = this; 
    } 
} 
+0

Gracias por t su propina Encontré la solución perfecta con la inyección de propiedad. Pero es sin IOtherService OtherService {set; } en IMainServices, porque cuando decore la propiedad con [Inject], Ninject le agrega la instancia correcta. –

+4

Esto no funciona. Con la última versión de Ninject, arrojará una 'StackOverflowException' si usa la inyección de propiedades para ambos, y arrojará la" dependencia cíclica detectada "si solo uno está usando inyección de propiedad (y la otra inyección de constructor). –

+4

Ah, pero * does * funciona siempre que utilice un ámbito que no sea el alcance transitorio (el valor predeterminado). –

2

Creo que no deberías usar propiedades o métodos de configuración para esto, es mejor usar Lazyness. El concepto de pereza resuelve el problema. El problema es que si tiene una dependencia circular entre los objetos, no está claro qué crear primero. La pereza se resuelve: una vez que el objeto se usa realmente (en general, este es el caso cuando se llama a un método público, debe existir). Por favor, evita las propiedades o incubadoras si puedes. Hacen que su objeto sea mutable (malo para la seguridad del hilo y no necesario cuando la dependencia debe ser inyectada solo una vez).

Usted constructores deberán tener este aspecto:

public OtherService(Lazy<IMainService> main) 
{ 
    this.main = main; 
} 

public MainClass(Lazy<IOtherService> s) 
{ 
    this.s = s; 
} 

Se puede describir estas dependencias de descanso bajo que el módulo de Ninject utilizando el método de carga llamando "ToMethod (" método lambda que crea el método perezoso basado en el método get ").

se presenta un claro ejemplo de cómo la pereza puede resolver dependencias circulares con Ninject aquí. también se describe un método de ayuda (BindLazy) para resolver su problema. https://www.codeproject.com/Tips/1171940/How-Ninject-Can-Help-in-Resolving-Circular-Depende

+0

Lo usé. ¡Esto estuvo bastante limpio! –

Cuestiones relacionadas