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


68

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;

   .....
}

有没有办法使我的伪造代码有效?

Answers:


92

您可以在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;

   .....
}

没有依赖项属性,是否可以使它工作?例如,IRepository>("Customer")应该在另一个实例注入ClientModel
燃情

1
@请确保使用API​​。为所需的每种注入样式变体为ClientModel创建一个单独的命名注册,然后解决对IClientModel的依赖性。与上面的IRepository相同。
克里斯·塔瓦雷斯

25

这是一个很晚的回复,但问题仍然出现在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>();

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

希望这可以帮助!


1
我比InjectionConstructor更喜欢这种模式,但是它不是隐藏在StrategyResolver后面的Service Locator吗?
MistyK '17

它的确看起来像某种形式的服务定位器,但我认为事实并非如此。您仅将StrategyResolver用作帮助程序来实现故事的策略模式结尾。StrategyResolver不是您的依赖项注入容器。如果您开始做一些不正常的事情,例如将所有依赖项作为命名依赖项加载并使用StrategyResolver查找它们,那么是的,在那种情况下,您实际上只是将其用作服务定位器反模式。如果您不滥用并坚持其宗旨,那么它将使您的代码漂亮而不依赖于DI技术。
TchiYuan '17

StrategyResolver在您的容器中注册为依赖项。依赖项是通过构造函数注入的。如果当前类已在容器中注册,则您的容器将能够通过查看类的构造函数参数来注入所需的依赖项。
TchiYuan '17

2
对此的一种改进(以限制滥用)可以是限制IStrategyResolver接口。基本上,不能解决任何问题,T您只能使用一种方法IActionHandler ResolveMyStrategy(string strategyName).
Botis

1
我的两分钱...我已经按照这些思路实现了一些东西,但是IUnityContainer我没有使用传递给解析器类型,而是使用了Func<string, ISomeInterface> resolveras作为构造函数参数。因此,您可以将分辨率委托给DI项目模块,从而可以与所使用的容器无关。注册:container.RegisterInstance<IStrategyResolver>(new StrategyResolver(key => container.Resolve<ISomeInteface>(key)))。同样,这种方法更加严格,可能会滥用服务定位器。
Miguel A. Arilla

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");

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


那会起作用,但是这意味着了解clientModel的级别还需要知道什么是存储库以及它的外观。我需要从知道“ clientModel”的级别上抽象出来的存储库。(事实上,clientModel整点是让库抽象到我的服务层。
Vaccano

1
我想直接注入构造函数。(这是这个问题的重点。)我只有2个IRepository映射。我正在寻找一种方法来帮助统一区分它们。
瓦卡诺2011年

@Kyle-在resolve调用上指定名称不是可传递的-解析<ClientModel,“ Client”>不会自动解析<IRepository,“ Client”>。容器中需要进行配置才能使用正确的名称。
克里斯·塔瓦雷斯

2

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.