Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)),
                };
    }
}
like image 432
James Skimming Avatar asked Oct 15 '12 12:10

James Skimming


3 Answers

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

}
like image 91
Anthony Johnston Avatar answered Nov 06 '22 03:11

Anthony Johnston


If you only need to use RouteLinker from ApiController derivates, you don't really need to go through all the DI hoops.

You can just create it within the Controller like this:

var linker = new RouteLinker(this.Request);

IMO, using DI with RouteLinker first becomes valuable when you need a RouteLinker further down the stack - but then again, I also only use RouteLinker as a Concrete Dependency...

like image 4
Mark Seemann Avatar answered Nov 06 '22 02:11

Mark Seemann


Thanks for adding the code sample. Based on what you have posted, you are running into your Bind/Rebind issue because you are issuing the _kernel.Bind<IResourceLinker> in the IHttpControllerActivtor.Create method every time.

You need to move the _kernel.Bind<IResourceLinker> to be registered the same way your are registering the rest of your bindings in the

...standard way, by standard I mean in the class generated by using the Ninject.MVC3 nuget package.

There should not be any need for the IResourceLinker to be binded multiple times, and this is why you are getting multiple instances, because the binding is firing every time a controller is created by the IHttpControllerActivator.

Update: Sorry that I missed the need for an HttpRequestMessage as a constructor argument, I would go with Anthony Johnson's answer on this one.

like image 2
Paige Cook Avatar answered Nov 06 '22 02:11

Paige Cook