传递IKernel

时间:2014-08-26 18:28:22

标签: ninject inversion-of-control

我有一个看起来像这样的控制器:

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

2 个答案:

答案 0 :(得分:1)

第一项改进是使用IResolutionRoot代替IKernelIResolutionRootIKernel的“服务定位器”部分。 IKernel来自IResolutionRootIBindingRoot。传递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();

现在我可以让我的服务接受一个I​​FooFactory:

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

令人惊讶的是,这一切都自动化了!