С Unity, как я могу вставить именованную зависимость в конструктор? - PullRequest
64 голосов
/ 13 августа 2011

Я дважды зарегистрировал 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;

   .....
}

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

Ответы [ 4 ]

81 голосов
/ 13 августа 2011

Вы можете настроить зависимости с именами или без них в 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;

   .....
}
23 голосов
/ 17 июня 2016

Это очень поздний ответ, но вопрос все еще появляется в 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 при добавлении новых стратегий в будущем.

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

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

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

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

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

Редактировать (2016-09-19): Для тех, кто может задаться вопросом, контейнер будет знать, что он пропустит себя, когда выIUnityContainer запрашивают *1032* как зависимость, как показано в сигнатуре конструктора 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>();

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

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

3 голосов
/ 13 августа 2011

Вы должны иметь возможность использовать 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");

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

0 голосов
/ 31 июля 2018

Не делайте этого - просто создайте class ClientRepository : GenericRepository { } и используйте систему типов.

...