3

Мы создаем приложение, которое имеет ряд точек интеграции с другими системами. Мы эффективно используем Unity для всех наших потребностей в инъекциях зависимостей. Весь бизнес-уровень был построен с использованием интерфейса, основанного на интерфейсе, причем фактическая реализация была внедрена во внешний корневой состав во время загрузки приложения.Абстрактная фабрика вписывается в Unity

Мы хотели обработать слой интеграции элегантным образом. Бизнес-классы и репозитории зависят от интерфейсов IIntegrationController<A, B>. Несколько реализаций IIntegrationController<A, B> представляют собой интеграцию с одной целевой системой в фоновом режиме - формирование слоя интеграции. В настоящее время мы соединяем все в корне композиции, одним выстрелом, в начале. Потребители этого интерфейса также регистрируются с соответствующими InjectionConstrutor и ResolvedParameter спереди. Большинство типов работают с PerResolveLifetime, а бизнес-классы, потребляющие IIntegrationController, также разрешаются для каждого контекста запроса отдельно.

См. Нижеприведенный код.

 // IIntegrationController Family 1 
     // Currently the default registration for IIntegrationController types injected into the business classes 
     container.RegisterType<IIntegrationController<A, B>, Family1-IntegrationController<A, B>>(); 
     container.RegisterType<IIntegrationController<C, D>, Family1-IntegrationController<C, D>>(); 

     // IIntegrationController Family 2 (currently not registered) 
     // We want to be able to register this, or manage this set of mapping registrations separately from Family 1, 
     // and be able to hook these up dynamically instead of Family-1 on a per-resolve basis 
     container.RegisterType<IIntegrationController<A, B>, Family2-IntegrationController<A, B>>(); 
     container.RegisterType<IIntegrationController<C, D>, Family2-IntegrationController<C, D>>(); 

     // Repository/Business Class that consume IIntegrationControllers. 
     // There is a whole family of IIntegrationController classes being hooked in, 
     // and there are multiple implementations for the family (as shown above). A typical AbstractFactory scenario. 
     container.RegisterType(typeof(Repository<Z>), new PerResolveLifetimeManager(), 
      new InjectionConstructor(
       new ResolvedParameter<IIntegrationController<A, B>>(), 
       new ResolvedParameter<IIntegrationController<C, D>>()) 
     ); 

Постановка задачи:

Мы хотим, возможность переключать всю семью IIntegrationController<A, B> во время выполнения. Когда бизнес-класс решается, мы хотим, чтобы он был введен с правильной версией IIntegrationController<A, B> на основе параметра запроса, доступного в контексте.

  • А «под названием» регистрация решение, основанное не будет масштабируемым, по двум причинам: (всего семейства классов интеграции должен быть включен, и это потребовало бы неуклюжим регистрации имен и условное разрешение в коде, делая его трудно поддерживать).
  • Решение должно работать даже тогда, когда имеет место цепочка/иерархия разрешений, то есть прямой потребитель IIntegrationController также разрешен через Unity, так как он динамически вводится в другой класс.
  • Мы опробовали DependencyOverride и ResolveOverride класс во время разрешения, но для этого потребуется целый набор разрешений Family-2 IIntegrationController, которые можно переопределить, вместо того, чтобы просто переключаться на весь слой.
  • Мы понимаем, что вместо того, чтобы вводить контроллер IIntegrationController непосредственно в бизнес-класс, возможно, потребуется ввести AbstractFactory, но мы не сможем добиться этого, и не уверены, где будет происходить регистрация и разрешение. Если бизнес-класс подключен к AbstractFactory, сначала мне нужно будет подключить нужный завод по разрешению,
  • Требуется ли это для переопределения InjectionFactory? This link предлагает подход, но мы не смогли заставить его работать плавно.

ответ

2

Что хорошего в вашем дизайне, так это то, что у вас уже есть правильные абстракции на месте. Вы используете общие абстракции, поэтому проблему можно решить, просто применив правильные шаблоны поверх уже разработанного вами дизайна SOLID.

Другими словами, использование прокси-сервера:

// This class should be considered part of your composition root. 
internal class IntegrationControllerDispatcher<TRequest, TResult> 
    : IIntegrationController<TRequest, TResult> 
{ 
    private readonly IUserContext userContext; 
    private readonly Family1_IntegrationController<A, B> family1Controller; 
    private readonly Family2_IntegrationController<A, B> family2Controller; 

    public IntegrationControllerDispatcher(
     IUserContext userContext, 
     Family1_IntegrationController<A, B> family1Controller, 
     Family2_IntegrationController<A, B> family2Controller) { 
     this.userContext = userContext; 
     this.family1Controller = family1Controller; 
     this.family2Controller = family2Controller; 
    } 

    public TResult Handle(TRequest request) { 
     return this.GetController().Handle(request); 
    } 

    private IIntegrationController<TRequest, TResult> GetController() { 
     return this.userContext.IsInFamily("family1")) 
      ? this.family1Controller 
      : this.family2Controller; 
    } 
} 

С помощью этого класса вы вся конфигурация может быть уменьшена до этого:

container.RegisterType<IUserContext, AspNetUserContext>(); 

container.RegisterType( 
    typeof(IIntegrationController<,>), 
    typeof(IntegrationControllerDispatcher<,>)); 

container.RegisterType(typeof(Repository<>), typeof(Repository<>)); 

Обратите внимание на следующее:

  • Примечание использование регистраций, которые выполняют открытое отображение. Вам не нужно регистрировать ВСЕ ваши закрытые версии по одному. Вы можете сделать это с помощью одной строки кода.
  • Также обратите внимание, что типы для разных семей не зарегистрированы . Unity может разрешить их автоматически, потому что наш IntegrationControllerDispatcher зависит от них напрямую. Этот класс является частью логики инфраструктуры и должен быть размещен внутри вашего корня композиции.
  • Обратите внимание, что решение использовать конкретную реализацию семейства не производится за время построения графа объекта; Это делается во время выполнения, потому что значение, определяющее это, является временем выполнения. Попытка определить это на момент построения графа объектов, только усложнит ситуацию и сделает намного сложнее проверить графики объектов.
  • Кроме того, это значение времени выполнения абстрагируется за вызовом функции и помещается за абстракцией (IUserContext.IsInFamily в этом случае, но это, конечно, пример).
+0

Реальная реализация жизни не совсем ясна для меня. Не могли бы вы добавить ссылку на какой-то код, который использует этот подход? Если у вас есть, конечно. – dmigo

Смежные вопросы