简体   繁体   中英

How do I use Ninject.Extensions.Factory to bind a objects from a generic interface?

I have the following interfaces:

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

I then have concrete implementations of IRequest and IResponse that are both used in a concrete IRequestHandler or IRequestHandler Object.

A concrete example is:

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

Currently, I inject the concrete request and handler into whatever class needs it via constructor injection:

private readonly GetEngineOptionsRequest _engineOptionsRequest;
private readonly GetEngineOptionsRequestHandler _engineOptionsRequestHandler;

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

But I found I have places where I need to be able to generate RequestHandlers on the fly. My research points towards the correct way to do this to avoid the service locator anti-pattern is to use a IFactory/Factory to create these ServiceHandlers on the fly. I am just not sure how to do that using Ninject, particularly the Ninject.Extensions.Factory extension.

The use cases show that for a simple factory (which I have done), You can do:

public interface IFooFactory
{
    Foo CreateFoo();
}

and then bind it in your boot strapper as:

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

and inject a IFooFactory into your constructor and use it create an instance of Foo.

In my case, I need to be able to create a factory capable of generating concrete objects from:

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

I am not sure how to do this, and how to bind it after the fact.

After a lot of playing around...

When you configure the kernel, you have to tell it how to create the types IRequestHandler<GetEngineOptionsRequest> and IRequestHandler<GetEngineOptionsRequest, GetEngineOptionsResult> :

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

Then create the factory interface. You can have generic or non-generic methods on it - I've included examples of both. Note that method names starting with 'Get' are special - read this . You could probably go down that route, but I won't here.

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

Having done that , you can register the factory:

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

And then use it...

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

As part of my experimenting, I was trying to:

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

(with the aim of being able to define the factory interface as

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

again see the link above).

However, this fails to compile, for the same reason as

IRequestHandler<IRequest, IResponse> test = new GetEngineOptionsRequestHandler();

ie there isn't an implicit conversion between IRequestHandler and GetEngineOptionsRequestHandler. If IRequestHandler were covariant (ie had 'out' types only), then everything would be happy. However this isn't the case, so we can't leverage any sort of variance on IRequestHandler - specific types only!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM