Я только начинаю с DI и модульного тестирования и наткнулся на загвоздку, которая, я уверен, не проблема для более опытных разработчиков:
У меня есть класс под названием MessageManager, который получает данные и сохраняет их в db. В той же сборке (проект в Visual Studio) я создал интерфейс репозитория со всеми методами, необходимыми для доступа к базе данных. Конкретная реализация этого интерфейса находится в отдельной сборке под названием DataAccess.
Таким образом, DataAccess нуждается в ссылке проекта на MessageManager, чтобы знать об интерфейсе репозитория. А MessageManager требуется ссылка проекта на DataAccess, чтобы клиент MessageManager мог внедрить конкретную реализацию интерфейса репозитория. Это конечно не разрешено
Я мог бы переместить интерфейс в сборку доступа к данным, но я считаю, что интерфейс репозитория должен находиться в той же сборке, что и клиент, который его использует.
Так что я сделал не так?


Я думаю, вам следует перенести интерфейс репозитория на сборку DataAccess. Тогда DataAccess больше не будет ссылаться на MessageManager.
Однако пока трудно сказать, потому что я почти ничего не знаю о вашей архитектуре ...
У вас есть только два варианта: добавить сборку для удержания интерфейса или переместить интерфейс в сборку DataAccess. Даже если вы разрабатываете архитектуру, в которой класс DataAccess может когда-нибудь быть заменен другим разработчиком (даже в другой сборке) интерфейса репозитория, нет причин исключать его из сборки DataAccess.
Вы должны отделить свой интерфейс от любой сборки. Размещение интерфейса вместе с потребителем или разработчиком противоречит цели наличия интерфейса.
Назначение интерфейса - позволить вам внедрить любой объект, реализующий этот интерфейс, независимо от того, принадлежит ли он той же сборке, к которой принадлежит ваш объект DataAccess. С другой стороны, вам нужно разрешить MessageManager использовать этот интерфейс без необходимости использования какой-либо конкретной реализации.
Поместите свой интерфейс в другой проект, и проблема решена.
Часто проблемы с циклическими ссылками можно решить, используя внедрение установщика вместо внедрения конструктора.
В псевдокоде:
Foo f = new Foo();
Bar b = new Bar();
f.setBar(b);
b.setFoo(f);
Вы можете оставить структуру в том виде, в котором она есть сейчас (без зависимости от MessageManager от DataAccess, которая вызывает проблему), а затем заставить MessageManager динамически загружать конкретную реализацию, требуемую во время выполнения, с использованием класса System.Reflection.Assembly.
Вы используете инверсию контейнера управления? Если так, то ответ прост.
Сборка C (или B) запустит приложение / запросит у контейнера MessageManager, который будет знать, как разрешить MessageManager и в IRepository.
Я не использую контейнер IoC, но теперь вижу, что это будет правильный путь. Это избавляет меня от необходимости создавать множество новых сборок, и у меня есть одно место, откуда можно внедрить все мои зависимости. теперь все начинает становиться на свои места - спасибо
Инверсия зависимостей находится в игре:
Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Абстракция, от которой зависят классы в сборке DatAccess, должна находиться в отдельной сборке от классов DataAccess и конкретной реализации этой абстракции (MessageManager).
Да сборок то больше. Лично для меня это не имеет большого значения. Больших минусов в лишних сборках не вижу.
Я запутался. Почему вы не можете использовать setClient (dataAccess) и setClient (messageManager) для них обоих? Почему это запрещено? Или вам нужна эта информация, известная в конструкторе?