Две реализации одного и того же интерфейса в конфигурации 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 и настоятельно рассмотрели редизайн, чтобы полностью избежать этой ситуации. Это сделает вашу жизнь и жизнь ваших коллег-разработчиков легче в долгосрочной перспективе.

Другие вопросы по тегам