Lorenzo
Lorenzo

Reputation: 29427

How do I bind generic types with inheritance using custom logic?

I have a generic interface IService which is concretely implemented by three or four implementations.

public interface IService {
   void HandleRequest( RequestData data );
}

public class AService : IService {
   public void HandleRequest( RequestData data ) {
   }
}

public class BService : IService {
   public void HandleRequest( RequestData data ) {
   }
}

The rules to choose a concrete implementation versus a different one are actually implemented into a method which analyze the input object (RequestData) and based on some data coming from the database choose the correct concrete class.

I need now to do constructor injection in a controller class and would like to configure Ninject to handle the instance creation of those objects like in the following sample

public class MyController : ApiController
{
    private readonly IService _service;

    public MyController( IService service ) {
        _service = service;
    }
}

Is there any way to write such an extension to support Ninject in the decision process? I am thinking to something like this

Bind<IService>().To<AService>().IfConditionIsTrue( new CustomProvider() );
Bind<IService>().To<BService>().IfConditionIsTrue( new CustomProvider() );

public class CustomProvider {
    public bool ApplyCustomLogic() {
        RequestData rd = GetParameter();
        [...]
        //custom logic implementation here..
    }
}

Also, how I can create an instance on the fly by leveraging ninject functionalities? For example, inside a method at a certain time, I need an instance of IService. How can I ask ninject to supply a concrete instance on the fly? I have only worked with constructor injection until now.

Upvotes: 1

Views: 54

Answers (1)

Steven
Steven

Reputation: 172835

I don't think you should 'pollute' your DI configuration with this. Instead solve this by using a proxy class that can dispatch the RequestData messages to the correct handler implementation:

public sealed class ServiceDispatcher : IService
{
    private readonly Kernel kernel;
    public ServiceDispatcher(Kernel kernel) {
        this.kernel = kernel;
    }

    public void HandleRequest(RequestData data) {
        Type implementationType;
        // custom logic to determine correct IService based on data here..
        // and resolve that implementation from the kernel.
        IService realService = (IService)this.kernel.Get(implementationType);
        realService.HandleRequest(data);
    }
}

Now you can bind your ServiceDispatcher class to IService and allow it to be injected into anyone who depends on IService. The dispatcher will ensure that the request is dispatched to the correct implementation.

But you might want to consider using a generic abstraction such as IService<T>. Take a look at this article for instance.

Upvotes: 2

Related Questions