目前我正在重构一个旧项目并尝试使用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
名称注入所有操作。
答案 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();
}