Короткий вопрос:
Учитывая, что библиотека гарантирует , используя конкретный контейнер IOC для своих внутренних объектов, когда приложение использует эту библиотеку, учитывая, что приложение гарантирует , используя контейнер IOC для подключения своих зависимостей, учитывая, что два Контейнеры разные, как они могут хорошо играть вместе?
Сценарий таков: в приложении определены классы, зависящие от типов из библиотеки. Поэтому, когда контейнер приложения пытается создать такой класс, ему необходимо знать, как разрешить тип, который находится в библиотеке.
Вот длинный вопрос:
Этот вопрос, кажется, задавался в разных формах и формах ранее на SO, но я не могу найти ответ, который мне нужен, поэтому я собираюсь взглянуть на него с гипотетическим _over_simplified_ конкретным примером.
Мы хотим написать библиотеку для регистрации, которую пользователи могут включить в свое решение как пакет, чтобы получить функциональность регистрации из коробки.
Общедоступные интерфейсы, которые предоставляет библиотека ..
public interface ILogger {}
public interface ITarget {}
Конкретные реализации
internal class Logger: ILogger { public Logger(ITarget target) {}}
internal class FileTarget : ITarget {}
Требования заключаются в том, что если пользователь включает наш пакет и определяет класс со свойством типа ILogger
или имеет аргумент ctor типа ILogger
, то наша библиотека отвечает за внедрение конкретной реализации для этого интерфейса в определенный пользователем учебный класс. По умолчанию этот внедренный регистратор отправляется в файловую систему для ведения журнала, потому что реализация по умолчанию ITarget
, внедренная в реализацию ILogger
, - это FileTarget
нашей библиотекой.
Если пользователь решит написать класс, реализующий интерфейс ITarget
, тогда наша библиотека будет использовать его для внедрения в класс Logger
и не будет использовать реализацию по умолчанию FileTarget
.
ТАК, что я хочу продемонстрировать, это их двунаправленная зависимость.
Наша библиотека зависит от сборок пользователя, поскольку ей необходимо сканировать сборки пользователя для загрузки любых точек расширения (например, реализации ITarget
) и внедрять их в свои собственные объекты перед любыми реализациями по умолчанию.
Сборки пользователя зависят от библиотеки, так как если пользователь решит определить класс с интерфейсом ILogger
в качестве зависимости, то этот пользовательский объект должен получить конкретную ссылку на этот интерфейс, предоставленный нашим пользователем во время выполнения. библиотека.
Простое решение: если пользователь и наша библиотека используют один и тот же контейнер IOC, тогда проблема решена. Но это сильное предположение. Что я хочу сделать, это
- Используйте контейнер IOC с библиотекой, которая лучше всего соответствует требованиям библиотеки, в моем случае это Ninject.
- Во время выполнения каким-то образом предоставьте пользователю механизм вызова через какой-либо API в мою библиотеку, который обеспечит запуск Ninject, сканирование пользовательских сборок и передачу всех данных с учетом всех точек расширения.
Пока все хорошо, это вполне достижимо, но тут начинается сложная часть.
- если пользователь также использует Ninject, то проблема автоматически решается, поскольку Ninject уже знает, как разрешать интерфейсы, живущие в нашей библиотеке. Но что, если пользователь решит использовать свой выбор контейнера IOC?
Я почти хочу определить какую-то функциональность дочернего контейнера в библиотеке с интерфейсом, подобным
public interface IDependencyResolvingModule { T Get<T>(); []T GetAll<T>(); }
и предоставьте реализацию, которая использует выбранный нашей библиотекой контейнер (т.е. Ninect) для разрешения типа, запрошенного в двух описанных выше методах.
Я хочу, чтобы контейнер IOC пользователя имел некоторые функциональные возможности, и если он не может разрешить зависимость (например, ILogger
), он должен подключиться к реализации IDependencyResolvingModule
и запросить зависимость.
Таким образом, наша библиотека может использовать свой выбор контейнера IOC, а код пользователя может разрешать зависимости, о которых ее контейнер IOC не имеет никакого понятия. Разве это решение не сработало бы, если бы IOC-контейнеры там каким-то образом обеспечивали функциональность для регистрации одноэлементных экземпляров любых IDependencyResolverModules
, найденных в сборках в каталоге исполняемой сборки, и когда они не могут разрешить тип, спросите какой-либо из одноэлементных модулей?
Но, за исключением решения, которое требует размещения любого другого контейнера IOC, как еще это можно решить? Итак, проблема в несколько строк заключается в том, что когда сторонняя сборка решает использовать контейнер IOC для своих внутренних устройств, это простое решение, так что эта библиотека может просто предоставить механизм для контейнера IOC, находящегося снаружи, для подключения и разрешения зависимостей. которые живут в библиотеке.