【问题标题】:Resolve different instances based on configuration根据配置解析不同的实例
【发布时间】:2014-11-17 19:06:09
【问题描述】:

我正在尝试使用 Ninject 作为 Windows 服务的 IoC 框架。 我有以下类和接口:

组装核心:

public class Orchestrator : IOrchestrator
{
    ...

    public Orchestrator(ITerminal terminal)
    {
        ...
    }

    ...
}

组装 Vx520:

public class Vx520 : ITerminal
{
    ...

    public Vx520(string comPort, int bauds, int dataBits, Parity parity, StopBits stopBits)
    {
        ...
    }

    ...
}

组装 Vx580:

public class Vx580 : ITerminal
{
    ...

    public Vx580(string ip, int port)
    {
        ...
    }

    ...
}

我的想法是使用ConfigurationSection 让用户配置他想要的终端和数量。例如,在下面的配置中,我应该得到 3 个 Orcestrator 实例,一个带有 Vx520 终端,另外两个带有 Vx580,每个都有它的自定义配置:

<Terminals>
  <add Type="Vx520" ComPort="COM3" Bauds="9600" DataBits="8" Parity="None" StopBits="One" />
  <add Type="Vx580" Ip="192.168.0.50" Port="33999"/>
  <add Type="Vx580" Ip="192.168.0.51" Port="33999"/>
</Terminals>

我的配置部分正在工作,IEnumerable&lt;TerminalConfiguration&gt; 作为最终输出。 有没有办法使用此配置列表动态解决 Orchestrator 类的依赖关系? 如果 Ninject 无法做到这一点,是否有其他 IoC 框架可以推荐给我?

提前谢谢你

【问题讨论】:

  • 你想将哪一个注入到Orchestrator?你是根据什么做出决定的?
  • @YuvalItzchakov 基于TerminalConfiguration 的列表。假设我在该列表中有 3 个项目,那么我想要 3 个 Orchestrator 每个项目都有一个基于配置的 Type 字段的终端

标签: c# ninject ioc-container


【解决方案1】:

Ninject(或大多数甚至所有 DI 容器)不能用于根据一些任意的“外部”信息来控制要实例化的对象数量。 您可以做的是拥有多个绑定,如下所示:

IBindingRoot.Bind<IOrchestrator>().To<Orchestrator>();
IBindingRoot.Bind<IOrchestrator>().To<Orchestrator>();
IBindingRoot.Bind<IOrchestrator>().To<Orchestrator>();

然后注入它们(类型为IEnumerable&lt;IOrchestrator&gt; 的ctor 参数或通过IResolutionRoot.GetAll&lt;IOrchestrator&gt;() 检索它们。这将导致恰好3 个实例。 您还可以有条件(上下文)绑定,在绑定中设置条件,例如:

IBindingRoot.Bind<ITerminal>().To<Vx520>()
    .When(x => Config.Terminal.Type == "VX520");
IBindingRoot.Bind<ITerminal>().To<Vx580>()
    .When(x => Config.Terminal.Type == "VX580");

当你注入 ITerminal / do IResolutionRoot.Get&lt;ITerminal&gt;() 时,你将得到一个 Vx520Vx580(或缺少绑定异常!),具体取决于 Config.Terminal.Type 的值。

现在,当然,您可以解析配置并相应地创建绑定:

foreach(ITerminalConfig config in Terminals)
{
    IBindingRoot.Bind<IOrchestrator>().To<Orchestrator>()
        .WithParameter(new ConstructorArgument("config", config, shouldInherit: true));
}

因此,您将拥有 3 个 IOrchestrator 绑定,每个绑定都有一个 ConstructorArgument 来保存它的配置。由于ConstructorArgumentshouldInherit 设置为true,因此IOrchestrator 树下的类型可以注入,也可以注入条件。例如,您可以执行以下操作:

IBindingRoot.Bind<ITerminal>().To<Vx580>()
    .When(ctx =>
    {
        ConstructorArgument configConstructorArgument = 
            ctx.Parameters
               .OfType<ConstructorArgument>()
               .Single(x => x.Name == "config");

        var config = (ITerminalConfig)configConstructorArgument.GetValue(null, null);
        return config.Type == "Vx520";
    });

并且还将ITerminalConfig 注入Vx520(这就是您获得IP & Port / comport & baudrate,.. 设置的方式)。


但老实说,与其创建多个Bind&lt;IOrchestrator&gt;().To&lt;Orchestrator&gt;() 绑定,不如明确地完成创建不是更好吗?每个使用IOrchestratorFactory 创建实例的服务初始化程序/应用程序启动步骤?

【讨论】:

  • 感谢您的回答。我解决了使用您的最后一个代码 sn-p 注入哪个终端的选择,以及使用 configuration.Terminals.Select(config =&gt; kernel.Get&lt;IOrchestrator&gt;(new ConstructorArgument("config", config, true))) 解决 Orchestrators 的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-22
  • 1970-01-01
相关资源
最近更新 更多