使用Unity,我如何将命名依赖注入到构造函数中

本文关键字:依赖 注入 构造函数 Unity 使用 | 更新日期: 2023-09-27 18:05:32

我在以下代码中注册了两次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;
   .....
}

有没有办法让我的假代码工作?

使用Unity,我如何将命名依赖注入到构造函数中

您可以在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形参的构造函数。解析该参数时,除类型外,还应使用名称"Client"进行解析。"

如果你想使用属性,你的例子几乎可以工作,你只需要改变属性名称:

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

这是一个很晚的回复,但这个问题仍然出现在谷歌上。

所以无论如何,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>();

第二个建议是同样的事情,但使用工厂设计模式。

希望这对你有帮助!

您应该能够使用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");

,它会给出你注册的类型

不要这样做-只需创建一个class ClientRepository : GenericRepository { }并使用Type系统。