Как мне добавить именованную зависимость в конструктор с помощью Unity?

У меня есть IRespository , зарегистрированный дважды (с именами) в следующем коде:

// Setup the Client Repository
IOC.Container.RegisterType<ClientEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Client", new InjectionConstructor(typeof(ClientEntities)));

// Setup the Customer Repository
IOC.Container.RegisterType<CustomerEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Customer", new InjectionConstructor(typeof(CustomerEntities)));

IOC.Container.RegisterType<IClientModel, ClientModel>();
IOC.Container.RegisterType<ICustomerModel, CustomerModel>();

Но затем, когда я захочу решить эту проблему (чтобы использовать IRepository ) Мне нужно выполнить ручное разрешение, например следующее:

public ClientModel(IUnityContainer container)
{
   this.dataAccess = container.Resolve<IRepository>(Client);

   .....
}

Я бы хотел разрешить его в конструкторе (точно так же, как IUnityContainer ). Мне нужен способ сказать, в какой именованный тип разрешить.

Примерно так: (ПРИМЕЧАНИЕ: Неверный код)

public ClientModel([NamedDependancy("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}

Есть ли способ заставить мой поддельный код работать?

67
задан BartoszKP 21 December 2018 в 10:37
поделиться

3 ответа

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

Чтобы указать контейнеру разрешить именованную зависимость, вам нужно использовать объект InjectionParameter. Для вашего примера ClientModel сделайте следующее:

container.RegisterType<IClientModel, ClientModel>(
    new InjectionConstructor(                        // Explicitly specify a constructor
        new ResolvedParameter<IRepository>("Client") // Resolve parameter of type IRepository using name "Client"
    )
);

Это говорит контейнеру «При разрешении ClientModel вызовите конструктор, который принимает один параметр IRepository. При разрешении этого параметра разрешите с помощью имя «Клиент» в дополнение к типу. "

Если вы хотите использовать атрибуты, ваш пример почти работает, вам просто нужно изменить имя атрибута:

public ClientModel([Dependency("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}
86
ответ дан 24 November 2019 в 14:39
поделиться

Это очень поздний ответ, но вопрос все еще появляется в Google.

Так или иначе, 5 лет спустя ...

У меня довольно простой подход. Обычно, когда вам нужно использовать «именованную зависимость», это потому, что вы пытаетесь реализовать какой-то шаблон стратегии. В этом случае я просто создаю уровень косвенности между Unity и остальной частью моего кода, называемый StrategyResolver, чтобы он не зависел напрямую от Unity.

public class StrategyResolver : IStrategyResolver
{
    private IUnityContainer container;

    public StrategyResolver(IUnityContainer unityContainer)
    {
        this.container = unityContainer;
    }

    public T Resolve<T>(string namedStrategy)
    {
        return this.container.Resolve<T>(namedStrategy);
    }
}

Использование:

public class SomeClass: ISomeInterface
{
    private IStrategyResolver strategyResolver;

    public SomeClass(IStrategyResolver stratResolver)
    {
        this.strategyResolver = stratResolver;
    }

    public void Process(SomeDto dto)
    {
        IActionHandler actionHanlder = this.strategyResolver.Resolve<IActionHandler>(dto.SomeProperty);
        actionHanlder.Handle(dto);
    }
}

Регистрация:

container.RegisterType<IActionHandler, ActionOne>("One");
container.RegisterType<IActionHandler, ActionTwo>("Two");
container.RegisterType<IStrategyResolver, StrategyResolver>();
container.RegisterType<ISomeInterface, SomeClass>();

Теперь, хорошо, что мне больше никогда не придется прикасаться к StrategyResolver при добавлении новых стратегии в будущем.

1120 Это очень просто. Очень чисто и я держал зависимость от Unity до строгого минимума. Единственный раз, когда я бы коснулся StrategyResolver, - это если я решу изменить контейнерную технологию, что вряд ли произойдет.

Надеюсь, это поможет!

Редактировать: Мне не очень нравится принятый ответ, потому что когда вы используете атрибут Dependency в конструкторе вашего сервиса, вы на самом деле сильно зависите от Unity. Атрибут Dependency является частью библиотеки Unity. В этот момент вы также можете передать IUnityContainer зависимость везде.

Я предпочитаю, чтобы мои классы обслуживания зависели от объектов, которыми я полностью владею, вместо того, чтобы иметь жесткую зависимость от внешней библиотеки повсюду. Также использование атрибута Dependency делает подписи конструкторов менее понятными и простыми.

Кроме того, этот метод позволяет разрешать именованные зависимости во время выполнения без необходимости жестко кодировать именованные зависимости в конструкторе, в файле конфигурации приложения или использовать InjectionParameter, которые являются всеми методами, которые требуют знать, какую именованную зависимость использовать при проектировании. время.

Редактировать (2016-09-19): Для тех, кто может задаться вопросом, контейнер будет знать, что он пропустит себя, когда вы запрашиваете IUnityContainer как зависимость, как показано в сигнатуре конструктора StrategyResolver.

Редактировать (2018-10-20): Вот еще один способ, просто с использованием фабрики:

public class SomeStrategyFactory : ISomeStrategyFactory
{
    private IStrategy _stratA;
    private IStrategy _stratB;

    public SomeFactory(IStrategyA stratA, IStrategyB stratB)
    {
        _stratA = stratA;
        _stratB = stratB;
    }

    public IStrategy GetStrategy(string namedStrategy){
        if (namedStrategy == "A") return _stratA;
        if (namedStrategy == "B") return _stratB;
    }
}

public interface IStrategy {
    void Execute();
}

public interface IStrategyA : IStrategy {}

public interface IStrategyB : IStrategy {}

public class StrategyA : IStrategyA {
    public void Execute(){}
}

public class StrategyB : IStrategyB {
    public void Execute() {}
}

Использование:

public class SomeClass : ISomeClass
{
    public SomeClass(ISomeStrategyFactory strategyFactory){

        IStrategy strat = strategyFactory.GetStrategy("HelloStrategy");
        strat.Execute();

    }
}

Регистрация:

container.RegisterType<ISomeStrategyFactory, SomeStrategyFactory>();
container.RegisterType<IStrategyA, StrategyA>();
container.RegisterType<IStrategyB, StrategyB>();
container.RegisterType<ISomeClass, SomeClass>();

Это второе предложение - то же самое, но с использованием шаблона фабричного проектирования.

Надеюсь, это поможет!

24
ответ дан 24 November 2019 в 14:39
поделиться

Вы должны иметь возможность использовать ParameterOverrides

var repository = IOC.Container.Resolve<IRepository>("Client");
var clientModel = IOC.Container.Resolve<ClientModel>(new ParameterOverrides<ClientModel> { {"dataAccess", repository } } );

. Редактировать: я не уверен, почему вы обмениваетесь UnityContainer - лично мы внедряем наши зависимости в конструктор (что нормально). "из того, что я видел). Но независимо от этого, вы можете указать имя в ваших методах RegisterType и Resolve.

IOC.Container.RegisterType<IRepository, GenericRepository>("Client");
IOC.Container.Resolve<IRepository>("Client");

и он даст вам тип, который вы зарегистрировали для этого имени.

3
ответ дан 24 November 2019 в 14:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: