如何使用Ninject.Extensions.Factory绑定通用接口中的对象?

时间:2013-08-28 18:13:23

标签: c# ninject

我有以下界面:

public interface IRequest
{
}

public interface IResponse
{
}

public interface IRequestHandler<in TRequest, out TResponse>
    where TRequest : IRequest
    where TResponse : IResponse
{        
    TResponse Execute(TRequest request);
}

public interface IRequestHandler<in TRequest> where TRequest : IRequest
{        
    void Execute(TRequest request);
}

然后我有IRequest和IResponse的具体实现,它们都在具体的IRequestHandler或IRequestHandler对象中使用。

一个具体的例子是:

 public class GetEngineOptionsRequestHandler : IRequestHandler<GetEngineOptionsRequest,GetEngineOptionsResult> 
{
}

目前,我通过构造函数注入将具体请求和处理程序注入到需要它的任何类中:

private readonly GetEngineOptionsRequest _engineOptionsRequest;
private readonly GetEngineOptionsRequestHandler _engineOptionsRequestHandler;

public OptionsParser(GetEngineOptionsRequest engineOptionsRequest, GetEngineOptionsRequestHandler engineOptionsRequestHandler)
    {            
        _engineOptionsRequest = engineOptionsRequest;
        _engineOptionsRequestHandler = engineOptionsRequestHandler;           
    }

但我发现我需要能够动态生成RequestHandlers的地方。我的研究指向正确的方法来避免服务定位器反模式是使用IFactory / Factory动态创建这些ServiceHandler。我只是不确定如何使用Ninject,特别是Ninject.Extensions.Factory扩展。

用例表明,对于一个简单的工厂(我已经完成),你可以这样做:

public interface IFooFactory
{
    Foo CreateFoo();
}

然后将它绑定在引导过滤器中:

kernel.Bind<IFooFactory>().ToFactory();

并将IFooFactory注入构造函数并使用它创建一个Foo实例。

就我而言,我需要能够创建一个能够从以下位置生成具体对象的工厂:

IServiceRequest<IRequest> and IServiceRequest<IRequest,IResponse>.

我不知道该怎么做,以及如何在事后绑定它。

1 个答案:

答案 0 :(得分:2)

经过大量的游戏......

配置内核时,您必须告诉它如何创建类型IRequestHandler<GetEngineOptionsRequest>IRequestHandler<GetEngineOptionsRequest, GetEngineOptionsResult>

kernel.Bind<IRequestHandler<GetEngineOptionsRequest>>().To<ConcreteImplementationOfThisInterface>();
kernel.Bind<IRequestHandler<GetEngineOptionsRequest, GetEngineOptionsResult>>().To<GetEngineOptionsRequestHandler>();

然后创建工厂界面。你可以使用泛型或非泛型方法 - 我已经包含了两者的例子。请注意,以“Get”开头的方法名称是特殊的 - 读取this。你可能会走这条路,但我不会在这里。

public interface IRequestHandlerFactory
{
    // Generic
    IRequestHandler<TRequest> CreateGeneric<TRequest>() where TRequest : IRequest;
    IRequestHandler<TRequest, TResponse> CreateGeneric<TRequest, TResponse>() where TRequest : IRequest where TResponse : IResponse;

    // Specific
    IRequestHandler<GetEngineOptionsRequest, GetEngineOptionsResult> CreateSpecific();
}

完成 后,您可以注册工厂:

kernel.Bind<IRequestHandlerFactory>().ToFactory();

然后使用它......

public OptionsParser(IRequestHandlerFactory factory)
{
    var test = factory.CreateGeneric<GetEngineOptionsRequest, GetEngineOptionsResult>();
    var test2 = factory.CreateSpecific();
}

作为我的实验的一部分,我试图:

 kernel.Bind<IRequestHandler<IRequest, IResponse>>().To<GetEngineOptionsRequestHandler>().Named("SomethingOrOther");

(目的是能够将工厂界面定义为

public interface IFactory
{
    IRequestHandler<IRequest, IResponse> GetSomethingOrOther();
}

再次看到上面的链接。)

但是,由于与

相同的原因,无法编译
IRequestHandler<IRequest, IResponse> test = new GetEngineOptionsRequestHandler();

即。 IRequestHandler和GetEngineOptionsRequestHandler之间没有隐式转换。如果IRequestHandler是协变的(即只有'out'类型),那么一切都会很开心。但事实并非如此,因此我们无法在IRequestHandler上使用任何类型的差异 - 仅限于特定类型!