С 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;
.....
}
Есть ли способ заставить мой фальшивый код работать?
Ответы
Ответ 1
Вы можете настроить зависимости с именами или без них в 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
. При разрешении этого параметра IRepository
разрешение с именем" Client "в дополнение к типу".
Если вы хотите использовать атрибуты, ваш пример почти работает, вам просто нужно изменить имя атрибута:
public ClientModel([Dependency("Client")] IRepository dataAccess)
{
this.dataAccess = dataAccess;
.....
}
Ответ 2
Это очень поздний ответ, но вопрос все еще появляется в 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
качестве зависимости, как показано в сигнатуре конструктора 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
Вы должны иметь возможность использовать 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");
и он даст вам тип, который вы зарегистрировали для этого имени.
Ответ 4
Не делайте этого - просто создайте class ClientRepository: GenericRepository { }
и используйте систему типов.