2010-08-18 4 views
9

Я пытаюсь найти правильный способ связать что-то вроде этого с ninject.Циклическая зависимость с 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>(); 
    } 
} 

Это дает мне исключение:

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. 

Я не знаю, как писать BaseModule. Мне нужен только один экземпляр MainClass и один экземпляр OtherClass (например, одиночные).

Я пробовал такие вещи:

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

Но с той же ошибкой.

И как написать привязку для использования только одного экземпляра для интерфейсов MainClass и IMainService?

Спасибо за ответы.

ответ

15

Как говорится в сообщении об ошибке, у вас есть циклическая зависимость между MainClass и OtherClass, так как вы не можете создать ее без экземпляра другого. В идеале вы должны изменить структуру иерархии классов, чтобы удалить это требование.

Если вы не можете, решение должно использовать инъекцию свойств для одного (или обоих) классов, например.

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

Спасибо за т его наконечник. Я нашел идеальное решение с инъекцией свойств. Но это без IOtherService OtherService {set; } в IMainServices, потому что, когда я украшаю свойство с помощью [Inject], Ninject добавляет к нему правильный экземпляр. –

+4

Это не работает. В последней версии Ninject он будет вызывать 'StackOverflowException', если вы используете инъекцию свойств для обоих, и будет генерировать« циклическую зависимость », если только один использует инъекцию свойств (и другую инъекцию конструктора). –

+4

Ах, но он * делает * работать до тех пор, пока вы используете область, отличную от временной области (по умолчанию). –

2

Я думаю, что для этого вам не следует использовать свойства или методы настройки, вам лучше использовать Lazyness. Проблема ленивости решает проблему. Проблема в том, что если у вас есть круговая зависимость между объектами, становится неясным, что создать первый. Lazyness разрешается: когда объект действительно используется (в общем, это тот случай, когда вызывается публичный метод, он должен существовать). Пожалуйста, избегайте свойств или сеттеров, если возможно. Они делают ваш объект изменчивым (плохо для безопасности потока и ненужного, когда зависимость должна вводиться только один раз).

Вам Конструкторы должны выглядеть следующим образом:

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

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

Вы можете описать эти ленивые зависимости в вас Ninject модуль с помощью метода Load с помощью вызова «ToMethod (» метод лямбда, который создает метод Ленивый, основанный на методе GET «).

Яркий пример того, как можно решить ленивости циклических зависимостей с Ninject здесь представлен. Он также описывает вспомогательный метод (BindLazy), чтобы решить вашу проблему. https://www.codeproject.com/Tips/1171940/How-Ninject-Can-Help-in-Resolving-Circular-Depende

+0

Я использовал это. Это было довольно аккуратно! –

Смежные вопросы