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名称。

走这条路的问题是,正如命名绑定的文档中提到的,它通常是服务位置反模式。

这里更好的方法是使用抽象工厂模式。

抽象工厂模式提供了一种封装具有共同主题但未指定其混凝土等级。

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

Ninject提供了一些实现这一点的方法,例如实现您自己的Providers或Factory方法。

在Ninjectv3中,您可以使用Factory接口扩展。这个文档链接有一大堆示例代码。以下是主要的一个来说明这个概念:

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();
}

最新更新