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

StackOverflow https://stackoverflow.com/questions/89959

  •  01-07-2019
  •  | 
  •  

Вопрос

Я только начинаю заниматься DI и модульным тестированием и столкнулся с проблемой, которая, я уверен, не составит труда более опытным разработчикам:

У меня есть класс MessageManager, который получает данные и сохраняет их в базу данных.В той же сборке (проект в Visual Studio) я создал интерфейс репозитория со всеми методами, необходимыми для доступа к базе данных.Конкретная реализация этого интерфейса находится в отдельной сборке DataAccess.

Поэтому DataAccess требуется ссылка на проект MessageManager, чтобы узнать об интерфейсе репозитория.А MessageManager нужна ссылка на проект DataAccess, чтобы клиент MessageManager мог внедрить конкретную реализацию интерфейса репозитория.Это, конечно, запрещено

Я мог бы переместить интерфейс в сборку доступа к данным, но я считаю, что интерфейс репозитория должен находиться в той же сборке, что и клиент, который его использует.

Так что же я сделал не так?

Это было полезно?

Решение

Используете ли вы инверсию контейнера управления?Если да, то ответ прост.

Сборка А содержит:

  • Менеджер сообщений
  • IRepository
  • ContainerA (добавьте MessageManager)

Сборка B содержит (и ссылку на сборкуA):

  • Репозиторий реализует IRepository
  • ContainerB расширяет ContainerA (добавляет репозиторий)

Сборка C (или B) запустит приложение/запросит контейнер для MessageManager, который будет знать, как разрешить MessageManager. и IRepository.

Другие советы

Вам следует отделить свой интерфейс от любой сборки.Размещение интерфейса вместе с потребителем или разработчиком противоречит цели наличия интерфейса.

Цель интерфейса — позволить вам внедрить любой объект, реализующий этот интерфейс, независимо от того, принадлежит ли это той же сборке, к которой принадлежит ваш объект DataAccess.С другой стороны, вам нужно разрешить MessageManager использовать этот интерфейс без необходимости использования какой-либо конкретной реализации.

Поместите свой интерфейс в другой проект, и проблема решена.

У вас есть только два варианта:добавьте сборку для хранения интерфейса или переместите интерфейс в сборку DataAccess.Даже если вы разрабатываете архитектуру, в которой класс DataAccess когда-нибудь может быть заменен другим реализатором (даже в другой сборке) интерфейса репозитория, нет причин исключать его из сборки DataAccess.

Я думаю, вам следует перенести интерфейс репозитория в сборку DataAccess.Тогда DataAccess больше не будет необходимости ссылаться на MessageManager.

Однако пока трудно сказать, поскольку я почти ничего не знаю о вашей архитектуре...

Часто проблемы с циклическими ссылками можно решить, используя внедрение установщика вместо внедрения конструктора.

В псевдокоде:

Foo f = new Foo();
Bar b = new Bar();
f.setBar(b);
b.setFoo(f);

Инверсия зависимостей находится в игре:

Модули высокого уровня не должны зависеть от модулей низкого уровня.Оба должны зависеть от абстракций.Абстракции не должны зависеть от деталей.Детали должны зависеть от абстракций.

Абстракция, от которой зависят классы в сборке DatAccess, должна находиться в отдельной сборке от классов DataAccess и конкретной реализации этой абстракции (MessageManager).

Да, это больше сборок.Лично для меня это не имеет большого значения.Я не вижу большого недостатка в дополнительных сборках.

Вы можете оставить структуру в том виде, в котором она у вас есть (без зависимости от MessageManager от DataAccess, которая вызывает проблему), а затем MessageManager динамически загружать конкретную реализацию, необходимую во время выполнения, используя System.Reflection.Assembly сорт.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top