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

Vac*_*ano 67 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>();
Run Code Online (Sandbox Code Playgroud)

但是当我想要解决这个问题(使用IRepository)时,我必须像这样做一个手动解决方案:

public ClientModel(IUnityContainer container)
{
   this.dataAccess = container.Resolve<IRepository>(Client);

   .....
}
Run Code Online (Sandbox Code Playgroud)

我想要做的是在构造函数中解析它(就像IUnityContainer).我需要一些方法来说明要解析的命名类型.

这样的事情:( 注意:不是真正的代码)

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

   .....
}
Run Code Online (Sandbox Code Playgroud)

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

Chr*_*res 87

您可以在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"
    )
);
Run Code Online (Sandbox Code Playgroud)

这告诉容器"在解析时ClientModel,调用带有单个IRepository参数的构造函数.在解析该参数时,除了类型之外,还要使用名称'Client'解析."

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

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

   .....
}
Run Code Online (Sandbox Code Playgroud)


Tch*_*uan 24

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

所以无论如何,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

注册:

container.RegisterType<IActionHandler, ActionOne>("One");
container.RegisterType<IActionHandler, ActionTwo>("Two");
container.RegisterType<IStrategyResolver, StrategyResolver>();
container.RegisterType<ISomeInterface, SomeClass>();
Run Code Online (Sandbox Code Playgroud)

现在,关于这一点的好处是,在将来添加新策略时,我永远不会再次触及StrategyResolver.

这很简单.非常干净,我将Unity的依赖性保持在最低限度.我唯一一次接触StrategyResolver是因为我决定改变容器技术,这是不太可能发生的.

希望这可以帮助!

编辑:我真的不喜欢接受的答案,因为当你Dependency在服务的构造函数中使用该属性时,你实际上对Unity有很强的依赖性.该Dependency属性是Unity库的一部分.那时你不妨IUnityContainer到处传递依赖.

我更喜欢让我的服务类依赖于我完全拥有的对象,而不是在整个地方都依赖于外部库.使用Dependency属性也使构造函数签名不那么干净和简单.

此外,此技术允许在运行时解析命名依赖项,而无需在构造函数,应用程序配置文件或使用中对命名依赖项进行硬编码,InjectionParameter这些都是需要知道在设计时使用什么命名依赖项的方法.

编辑(2016年9月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() {}
}
Run Code Online (Sandbox Code Playgroud)

用法:

public class SomeClass : ISomeClass
{
    public SomeClass(ISomeStrategyFactory strategyFactory){

        IStrategy strat = strategyFactory.GetStrategy("HelloStrategy");
        strat.Execute();

    }
}
Run Code Online (Sandbox Code Playgroud)

注册:

container.RegisterType<ISomeStrategyFactory, SomeStrategyFactory>();
container.RegisterType<IStrategyA, StrategyA>();
container.RegisterType<IStrategyB, StrategyB>();
container.RegisterType<ISomeClass, SomeClass>();
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助!

  • 对此的改进(为了限制滥用)可能是限制IStrategyResolver接口.基本上,不是能够解决任何`T`,你可以只有一个方法`IActionHandler ResolveMyStrategy(string strategyName). (2认同)
  • 我的两分钱……我已经按照这些思路实现了一些东西,但是我没有将 `IUnityContainer` 传递给解析器类型,而是使用了一个 `Func&lt;string, ISomeInterface&gt; resolver` 作为构造函数参数。因此,您可以将分辨率委托给 DI 项目模块,从而与所使用的容器无关。注册:`container.RegisterInstance&lt;IStrategyResolver&gt;(new StrategyResolver(key =&gt; container.Resolve&lt;ISomeInteface&gt;(key)))`。此外,这种方法对可能的服务定位器误用更具限制性。 (2认同)