Search code examples
dependency-injectionninjectasp.net-web-apihyprlinkr

How to set up Ninject DI to create Hyprlinkr RouteLinker instances


I have an MVC4 Web API project and I making use of Mark Seemann's Hyprlinkr component to generate Uris to linked resources. (Customer -> Addresses for example).

I have already followed Mark's guide on Dependency injection with Web API (changing appropriately for Ninject) bit I can't quite work out what I should do to inject a IResourceLinker into my controllers.

Following Mark's guide my IHttpControllerActivator.Create create method looks like this:

IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
    var controller = (IHttpController) _kernel.GetService(controllerType);

    request.RegisterForDispose(new Release(() => _kernel.Release(controller)));

    return controller;
}

It is in this method that the Hyprlinkr readme suggests to create the RouteLinker. Unfortunately I'm not sure how to register this with Ninject.

I can't just bind like below, as this results in multiple bindings:

_kernel.Bind<IResourceLinker>()
    .ToMethod(context => new RouteLinker(request))
    .InRequestScope();

I've got rebind working like this:

_kernel.Rebind<IResourceLinker>()
    .ToMethod(context => new RouteLinker(request))
    .InRequestScope();

But I'm concerned that changing the ninject binding graph is potentially a bad thing to do on every request.

What is the best way to achieve this?


Update following the request from Paige Cook

I'm using rebind here:

IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
    _kernel.Rebind<IResourceLinker>()
        .ToMethod(context => new RouteLinker(request))
        .InRequestScope();

    var controller = (IHttpController) _kernel.GetService(controllerType);

    request.RegisterForDispose(new Release(() => _kernel.Release(controller)));

    return controller;
}

IHttpControllerActivator.Create is called on every request. The rest of the bindings are made in the standard way, by standard I mean in the class generated by using the Ninject.MVC3 nuget package.

My controller looks like this:

public class CustomerController : ApiController
{
    private readonly ICustomerService _customerService;
    private readonly IResourceLinker _linker;

    public CustomerController(ICustomerService customerService, IResourceLinker linker)
    {
        _customerService = customerService;
        _linker = linker;
    }

    public CustomerModel GetCustomer(string id)
    {
        Customer customer = _customerService.GetCustomer(id);
        if (customer == null)
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
        }

        return
            new CustomerModel
                {
                    UserName       = customer.UserName,
                    Firstname      = customer.Firstname,
                    DefaultAddress = _linker.GetUri<AddressController>(c => c.Get(customer.DefaultAddressId)),
                };
    }
}

Solution

  • Register a delegate Function to give you the linker

    _kernel.Bind<Func<HttpRequestMessage, IResourceLinker>>()
        .ToMethod(context => (request) => new RouteLinker(request));
    

    Inject the delegate

    readonly Func<HttpRequestMessage, IResourceLinker> _getResourceLinker;
    
    public controller(Func<HttpRequestMessage, IResourceLinker> getResourceLinker) {
    
        _getResourceLinker = getResourceLinker;
    }
    

    Use in your actions

    public async Task<Thingy> Get() {
    
        var linker = _getResourceLinker(Request);
        linker.GetUri( ... )
    
    }