Внедрение зависимостей с циклической зависимостью

#c# #dependency-injection #inversion-of-control

#c# #внедрение зависимостей #инверсия управления

Вопрос:

Позвольте мне иметь два очень простых объекта, таких как:

 public class View
{
    public View(Controller controller)
    {
        // Use the model exposed by the controller here
    }
}

public class Controller
{
    private readonly IView view;

    public Controller()
    {
        this.view = new View(this);
    }

    public Controller(View v)
    {
        this.view = v;
    }
}
  

Позже я решаю внедрить объект View в контроллер через DI, но там у меня циклическая зависимость (т. Е. Я не могу использовать var ctrl = new Controller(new View(ctrl)); ). Как бы вы поступили с внедрением зависимости в этом случае?

Комментарии:

1. Собираетесь ли вы иметь несколько представлений для одного контроллера?

2. Нет, только один. Однако в этом случае я пытаюсь избежать внедрения свойств.

3. Итак, зачем добавлять разрешение DI для просмотра? Между представлением и контроллером будет соотношение 1: 1. Извините, я не вижу в этом смысла.

4. Извините, теперь я понял, что вы имели в виду (я думал, вы спрашивали об одном экземпляре). Да, будет несколько представлений (gui, cli и т.д.)

5. Затем я предлагаю использовать ctor like Controller(IView view) , удалить controller параметр из View и добавить свойство для установки Controller экземпляра View .

Ответ №1:

Наиболее распространенным решением является использование свойства зависимости для решения циклических зависимостей. т.Е. Создайте новое свойство в одном из классов и позвольте контейнеру IoC назначить его.

Если вы используете Unity, вы должны добавить [Dependency] к этому свойству.

Примечание: представление не должно иметь зависимости от контроллера. Он вообще не должен знать об этом.

Обновление в ответ на комментарий

Вы не можете. Это проблема с циклическими зависимостями. Единственное другое решение — использовать композицию. Это означает выделение общей функциональности в отдельный класс и включение ее как в контроллер, так и в представление.

Комментарии:

1. Да, на самом деле представление использует только модель (для привязки данных), предоставляемую контроллером, в данном случае постарался быть кратким. Кстати, я пытаюсь избежать внедрения свойств (поскольку зависимости имеют решающее значение для функциональности класса).

2. да, вы правы в этом, похоже, мне придется использовать prop-inj. или другое экзотическое решение.

3. Ваш собственный ответ — это вариант шаблона поиска служб. Вы также можете создать a ViewFactory , который принимает контроллер в качестве параметра и принимает его в качестве параметра конструктора в контроллере.

4. Спасибо за отличную ViewFactory идею, я должен был подумать об этом раньше.. продвигаемся вперед с заводской реализацией.

Ответ №2:

Я действительно нашел хорошее решение, используя Ninject.

 public class Controller
{
    private readonly View view;

    public Controller(ViewModule viewModule)
    {
        using (IKernel kernel = new StandardKernel(viewModule))
        {
            this.view = kernel.Get<View>(new ConstructorArgument("controller", this);
        }
    }
}
  

Где ViewModule является предварительно настроенным модулем Ninject для разрешения конкретной зависимости представления (GUI, CLI и т. Д.). Небольшая проблема здесь заключается в том, что теперь я зависим от конкретной платформы DI.

Ответ №3:

Вы вообще не сможете этого сделать с помощью constructor-injection, если вы измените конструктор контроллера на

 public Controller(IView view)
  

в каком порядке вы бы создали два объекта?
Для представления требуется экземпляр контроллера, а контроллеру требуется представление.
Тем не менее, вы можете сделать свойство IView контроллера общедоступным и установить свойство после создания (некоторые DI-контейнеры могут сделать это для вас автоматически при установке правильного атрибута).