我有一个看起来像这样的控制器:
public class MyController : MyController
{
private readonly IMyService service;
public MyController(IMyService _service)
{
service = _service;
}
[HttpGet]
public BaseFoo GetFoo(Guid id)
{
return service.GetFoo(id);
}
}
Ninject将为我注入IMyService
。现在在IMyService
的实现中,我希望能够创建从BaseFoo
派生的对象。所以我最初有这样的事情:
public MyService : IMyService
{
public BaseFoo GetFoo(Guid id)
{
Type t = // some code that determines which type we actually need
return (BaseFoo) Activator.CreateInstance(t);
}
}
哪个工作正常。但是现在我想做的是BaseFoo
的不同实现依赖于不同的服务(或没有服务)。例如,我可能有:
public SomeFoo : BaseFoo
{
public SomeFoo(IOtherService _service)
{
}
}
理想情况下,我希望让Ninject处理依赖注入,但我不确定处理这种情况的正确方法是什么。 MyService
需要处理创建BaseFoo
个对象,所以我想添加一个这样的构造函数:
public MyService(IKernel kernel)
哪个内核被注入然后在GetFoo
而不是Activator
我可以:
return (BaseFoo) kernel.Get(t);
这很有效。但感觉不对。它需要MyService
才能了解Ninject,这似乎是错误的。
这里有更好的模式吗?
编辑:BatteryBackupUnit建议使用ninject工厂扩展程序,听起来这可能是一个更好的解决方案,但我不太清楚如何在这种情况下使其工作。如果我创建一个IFooFactory,我想它需要看起来像这样:
public interface IFooFactory
{
BaseFoo CreateFoo(Type t);
}
但是这会尝试创建一个BaseFoo
的实例,将一个类型传递给构造函数,这不是我需要的。 BaseFoo
是抽象的,我需要一个工厂,它将创建一个t
类型的具体实例,该实例派生自BaseFoo
。
答案 0 :(得分:1)
第一项改进是使用IResolutionRoot
代替IKernel
。
IResolutionRoot
是IKernel
的“服务定位器”部分。 IKernel
来自IResolutionRoot
和IBindingRoot
。传递IKernel
将允许用户创建您不想要的新绑定。
对您的建议设计的第二个改进是使用ninject factory extension而不是IKernel
/ IResolutionRoot
。
它从界面自动创建工厂,例如,如果您有:
public class BaseFoo
{
public BaseFoo(IServiceDependency service, string parameter) { }
}
你可以有一家工厂:
public interface ISpecialFooFactory
{
BaseFoo Create(string parameter);
}
其中,工厂方法的参数名称需要与构造函数参数名称匹配。 所有未传递给工厂的ctor参数都会自动注入/解决。
现在,这仍然与ServiceLocator(反)模式有一些不利之处。 关于此事,请参阅Mark Seeman's blog post(我也建议您阅读评论)。
理想情况下,您可以调整您的设计,以便一次性实例化整个对象图,并将“运行时参数”作为方法参数而不是构造函数参数传递。
如果那是不可能的,我仍然会尝试将对象创建保持在最低限度,并使用ninject的工厂扩展来完成。
答案 1 :(得分:1)
根据BatteryBackupUnit非常有用的反馈回答我自己的问题,这就是我最终要做的事情。我按建议使用了ninject factory extension并创建了一个IFooFactory
,如下所示:
public interface IFooFactory
{
T CreateFoo<T>();
}
并且受到如此约束:
kernel.Bind<IFooFactory>().ToFactory();
现在我可以让我的服务接受一个IFooFactory:
public MyService(IFooFactory _factory)
{
factory = _factory;
}
GetFoo
的实际实现有点棘手,因为我需要提供泛型类型。幸运的是Jon Skeet has the answer:
public BaseFoo GetFoo(Guid id)
{
Type t = // some code that determines which type we actually need
MethodInfo mi = factory.GetType().GetMethod("CreateFoo");
MethodInfo generic = mi.MakeGenericMethod(type);
return (BaseFoo)generic.Invoke(factory, null);
}
令人惊讶的是,这一切都自动化了!