Две реализации одного и того же интерфейса в конфигурации JSON Autofac
Мне нужно включить позднюю привязку в приложении, и я хочу иметь возможность явно настраивать службы, используя файл конфигурации JSON.
У меня есть интерфейс IDependency
и два класса DependencyOne
а также DependencyTwo
которые оба реализуют интерфейс. у меня тоже есть SomeService
класс, имеющий конструктор со следующей подписью:
public SomeService(IDependency dependency1, IDependency dependency2)
Я хочу сделать инъекцию DependencyOne
за dependency1
, а также DependencyTwo
за dependency2
,
Как я могу настроить это исключительно в конфигурации JSON, без использования каких-либо атрибутов в коде? Это вообще возможно?
Требуется отсутствие атрибута, поскольку сборка с поздним связыванием не должна зависеть от AutoFac.
Заранее спасибо.
Обновление: решение
Ответ Тревиса ниже содержит ссылку на FAQ, которая привела меня к приемлемому решению. Используйте "маркерные" интерфейсы, например IDependencyOne : IDependency
а также IDependencyTwo : IDependency
, а потом SomeService(IDependencyOne dependency1, IDependencyTwo dependency2)
, Мне не очень нравится то, что теперь универсальный декоратор для IDependency
необходимо реализовать все маркеры, скажем LoggingDecorator : IDependencyOne, IDependencyTwo
если я хочу использовать его в SomeService
, но пока маркеры остаются пустыми, это не большая проблема. Таким образом, мне не нужно навязывать зависимости от dll-файлов Autofac в сборке с поздним связыванием, в то время как DI-файл настроен в файле JSON.
1 ответ
Если у вас есть две разные реализации одного и того же интерфейса, которые не могут обрабатываться одинаково, это запах кода. Существует часто задаваемый вопрос о том, как обойти эту проблему, но краткий ответ: вы не сможете буквально указать два разных экземпляра одного и того же интерфейса, как это, без некоторой ручной работы. Не существует механизма для легкого подключения, потому что это проблема дизайна.
Найдите минутку, чтобы прочитать FAQ, чтобы узнать, почему и дополнительные идеи о том, как решить эту проблему, помимо того, что я показываю здесь.
Тем не менее, давайте предположим, что вы не можете изменить интерфейс IDependency
так как обычно это камень преткновения для людей. Давайте также предположим, что вы не можете просто поставить DependencyOne
а также DependencyTwo
прямо в конструкторе... по любой причине. (И то, и другое было бы первым местом, где я решил бы решить эту проблему, а не пытаться усложнить мою работу с DI, но, опять же, скажем, ради аргумента, что это не вариант.)
Я бы, вероятно, зарегистрировал каждый экземпляр в конфигурации с ключом метаданных, который можно использовать позже.
{
"components": [{
"type": "MyAssembly.DependencyOne, MyAssembly",
"services": [{
"type": "MyAssembly.IDependency, MyAssembly"
}],
"metadata": [{
"key": "type",
"value": "One",
"type": "System.String, mscorlib"
}]
}, {
"type": "MyAssembly.DependencyTwo, MyAssembly",
"services": [{
"type": "MyAssembly.IDependency, MyAssembly"
}],
"metadata": [{
"key": "type",
"value": "Two",
"type": "System.String, mscorlib"
}]
}]
}
Итак, у нас есть два компонента, каждый из которых предоставляет один и тот же интерфейс, и у каждого есть ключ метаданных: One
или же Two
соответственно.
В вашем классе вы можете использовать эти метаданные.
public class SomeService
{
readonly IEnumerable<Meta<IDependency>> _dependencies;
public SomeService(IEnumerable<Meta<IDependency>> dependencies)
{
_dependencies = dependencies;
}
public void DoSomething(string parameter)
{
var dep = _dependencies.First(a => a.Metadata["type"].Equals(parameter));
dep.DoSomething();
}
}
Идея в том, что вы можете выбрать подходящую вещь, используя метаданные. Очевидно, приспособить это к вашим собственным потребностям; возможно, это не параметр от вызывающей стороны, а что-то в вашем коде в другом месте; концепция все еще остается в силе.
Опять же, однако, я не могу рекомендовать достаточно сильно, чтобы вы проверили FAQ и настоятельно рассмотрели редизайн, чтобы полностью избежать этой ситуации. Это сделает вашу жизнь и жизнь ваших коллег-разработчиков легче в долгосрочной перспективе.