Ninject:绑定时定义命名约束

时间:2017-01-25 16:13:23

标签: c# dependency-injection ninject

目前我正在重构一个旧项目并尝试使用Ninject实现依赖注入。

我遇到了一个或多或少的简单问题。我为此找到了一个有效的解决方案,但我不确定这是否是解决此问题的最佳方法。

我试着尽可能准确地解释这种情况:

我有一个界面ITool

public interface ITool 
{
    string Caption { get; set; }
    string Name { get; }

    IAction[] Actions { get; }
}

还有一个抽象类AbstractTool(具体内容与问题无关)

此外,我有派生类GenericTool

public class GenericTool : AbstractTool 
{
    private readonly string furtherInformation;

    public override string FurtherInformation
    { 
       get { return furtherInformation; } 
    }

    public GenericTool (string furtherInformation, string caption, string name, Action[] actions)
        : base(caption, name, actions)
    {
       this.furtherInformation= furtherInformation;
    }        
}

有一次使用了这个GenericTool,并且像这样实例化几次:

new OtherObject(
{
   new List<GenericTool>
   {
      new GenericTool("info1","caption1","name1", new IActions[]
         { new Action1(), new Action2()}),
      new GenericTool("info2","caption2","name2", new IActions[]
         { new Action3(), new Action4()}),
        ...
   }
}...

我只想通过一个电话来解决这个问题:

kernel.Get<OtherObject>();

但我不确定如何将不同的操作(Action1,2,3,4)绑定到他们将正确解析为归属GenericTool

我的解决方案是创建一个GenericTool的派生类,并使用Ninject的NamedAttribute

public class SpecialTool1 : GenericTool
{
    public SpecialTool1([Named("SpecialTool1Action")] IAction[] actions)
          : base("info1", "caption1","name1", actions)
    {}
}

public class SpecialTool2 : GenericTool
{
    public SpecialTool2([Named("SpecialTool2Action")] IAction[] actions)
        : base("info2", "caption2","name2", actions)
    {}
}

行动的绑定:

Bind<IAction>.To<Action1>().Named("SpecialTool1Action");
Bind<IAction>.To<Action2>().Named("SpecialTool1Action");
Bind<IAction>.To<Action3>().Named("SpecialTool2Action");
Bind<IAction>.To<Action4>().Named("SpecialTool2Action");

有了这个,我得到了所需的结果。但是我必须创建很多像SpecialTool1这样的小类。

所以我的第二个工作是:

Bind<IAction>.To<Action1>().Named("SpecialTool1Action");
Bind<IAction>.To<Action2>().Named("SpecialTool1Action");

Bind<ITool>().To<GenericTool>()                        
   .WithConstructorArgument("furtherInformation","info1")
   .WithConstructorArgument("caption", "caption1")
   .WithConstructorArgument("name", "name1")
   .WithConstructorArgument("actions", GetLateNamedConstructorArgument);

函数GetLateNamedConstructorArgument

private IAction[] GetLateNamedConstructorArgument(IContext context)
{
    IEnumerable<IAction> actions= context.Kernel.
            GetAll<IAction>("SpecialTool1Action");
    return actions.ToArray();
}

使用最后一个解决方案,我得到了相同的结果,而没有创建几十个类。

但有没有更好的方法来解决这个问题?我可以用其他方式声明所需绑定的名称吗?可能还有其他完整的工作吗?

修改

我想象这样的事情:

Bind<ITool>().To<GenericTool>()
   ...
.WithConstructorArgument("actions", [Named("SpecialTool1Action")]);

Ninject使用SpecialTool1Action名称注入所有操作。

1 个答案:

答案 0 :(得分:0)

沿着这条路线走下去的问题是,如named bindings的文档所述,它通常是Service Location anti-pattern

这里更好的方法是使用Abstract Factory模式。

  

抽象工厂模式提供了一种封装一组的方法   具有共同主题但未指定其主题的各个工厂   具体课程。

基本上,不是让DI容器遍布您的代码来满足您的复杂需求,而是从它请求工厂并使用它们来创建所需的实例。

Ninject提供了一些实现方法,例如implementing your own Providers or Factory Methods.

在Ninject v3中,您可以使用Factory interface扩展名。该文档链接有一大堆示例代码。以下是说明这一概念的主要内容:

public class Foo
{
    readonly IBarFactory barFactory;

    public Foo(IBarFactory barFactory)
    {
        this.barFactory = barFactory;
    }

    public void Do()
    {
        var bar = this.barFactory.CreateBar();
        ...
    }
}

public interface IBarFactory
{
    Bar CreateBar();
}