使用Unity如何将命名依赖项注入构造函数?

时间:2011-08-12 21:58:21

标签: c# inversion-of-control unity-container

我在以下代码中注册了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 个答案:

答案 0 :(得分:79)

您可以在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“解析。”< / p>

如果您想使用属性,您的示例几乎可以使用,您只需要更改属性名称:

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

   .....
}

答案 1 :(得分:22)

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

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

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

希望这有帮助!

答案 2 :(得分: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");

它将为您提供您为该名称注册的类型。

答案 3 :(得分:0)

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