1

我有以下接口:

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>.

我不确定如何做到这一点,以及事后如何绑定它。

4

1 回答 1

2

玩了好久...

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

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

然后创建工厂接口。您可以在其上使用通用或非通用方法 - 我已经包含了这两种方法的示例。请注意,以“Get”开头的方法名称是特殊的 - 阅读内容。你可能会走那条路,但我不会在这里。

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 上利用任何类型的差异——仅限特定类型!

于 2013-08-28T20:55:59.667 回答