Using Ninject with the latest ASP.NET Web API source - StrathWeb

Strath

Using Ninject with the latest ASP.NET Web API source

Due to changes in System.Web.Http, the old ways don't work anymore

After ASP.NET Web API was beta-released in February, a number of blog posts came out explaining how to plug Ninject (and other IoC-s, for that matter) to your application through the use of the IDependencyResolver adapter. There was even a NuGet Ninject extension for that (here).

However, these approaches don’t work anymore if you are using the latest ASP.NET Web API (and release candidate) – either from Codeplex source or nightly build packages.

Let’s explore why is that and what you need to do now to be able to use Ninject with ASP.NET Web API.

Important Update: 02 June 2012

As mentioned in the comments, for the builds between 16 May and 02 June (not RC, and not any of the latest so don’t worry), there are issues with methods accepting parameters (i.e. Get(int id)). This has been raised at Codeplex here by Tim (from the comments), I added a repro project there, and turns out, as posted by Brad Wilson, that:
The issue was that we had a concrete internal type in the services container, and Ninject (and surely several other DI containers) would eagerly try to create an instance of that type when they shouldn’t have. Shifted to an internal interface instead to prevent the eager creation.

This has been modified in the ASP.NET Web Stack commit c7e396eec520. So if you are using a build from the above mentioned dates and encounter this problem, make sure to take the latest nightlies or the latest build from Codeplex. Thanks to the readers – Mark, Tim and everyone involved, and especially to Brad for having a look at this!

The old and new IDependencyResolver

The old (beta-compatible) approach for using Ninject with Web API was based on implementing a class deriving from IDependencyResolver. That interface looked like this in beta:

However, that has changed, and in the current version of Web API (and, as far as I know, the same will be in the release candidate), this interface is as follows:

With IDependencyScope being:

And IDisposable, obviously:

All the Ninject dependency resolvers targeted at beta will not work now, because there are a couple more interfaces and methods that need to be implemented and catered for. The change is very well explained by Mike Wasson here. Let’s use his Unity example to provide support for Ninject.

We’ll need two classes – one that implements IDependencyScope and the other that implements.

NinjectScope : IDependencyScope

This is our scope container – think of it as a child scope. Our NinjectScope class is going to implement IDependencyScope.

We are going to use this to get service of specific type. To adhere to the requirements of IDependencyScope, we implement IDisposable. To resolve the dependencies, we use IRequest, to avoid depndency on ResolutionExtensions.

NinjectResolver : NinjectScope, IDependencyResolver

This is going to be our entry point. Think of it as global scope, which will be responsible for passing the activation block to the child scope.

We implement IDependencyResolver here, and the rest of the interface is catered for by the fact that we inherit from our previously created NinjectScope.

Now things are ready to roll.

Enabling it in the web application

So, if you have Ninject.MVC3 added to you solution, you’d go to NinjectWebCommon.cs (or Global.asax, depending on you activation preferences) and would add this (after you register your services):

Please note that from the Web API Beta, the configuration changed its place and type, as it’s now a property of GlobalConfiguration.Configuration.

Trying it out

In my case I have a simple pair of IUrlRepository and UrlRepository. It’s the same stuff I used here before.

So I register the service:

And then set up my ApiController:

Now I can fire up the controller, and voila:

IoC with Ninject, running smoothly with ASP.NET Web API!

Source code

I put up source code for the two classes on github (no point in adding entire project). It would make sense that this is merged into Ninject.Web.WebAPI on Nuget, but since that project is dependent on ASP.NET Web API (the NuGet version), I guess it won’t happen until ASP.NET Web API RC is available on Nuget (unless that dependency is removed).

Anyway, till next time!

- source on GitHub

Be Sociable, Share!

  • dark_knight

    Great stuff, been looking for this! Thanks Filip and keep up writing!

  • Pingback: Dew Drop – May 29, 2012 (#1,336) | Alvin Ashcraft's Morning Dew

  • Pingback: Notebook Heavy » Integration Test ASP.NET Web API with StructureMap » Notebook Heavy

  • Mark

    This breaks as soon as your method accepts a parameter such as:

    public IQueryable Get(string id)
    {
    return _repo.GetAll();
    }

    The above will generate an exception, ‘The lazily-initialized type does not have a public, parameterless constructor.’

    • Filip W

      Hi

      You don’t have a return type on this method, just IQueryable.
      public Url Get(int id)
      {
      return _repo.Get(id);
      }

      This works just fine.
      cheers
      /f

      • Mark

        That was a typo. I’m getting an exception with the latest nightly builds. If the parameter is fine there’s no issue but if not I see an exception.

        An error has occurred.The lazily-initialized type does not have a public, parameterless constructor.System.MissingMemberException at System.Lazy1.CreateValue()
        at System.Lazy
        1.LazyInitValue()
        at System.Lazy1.get_Value()
        at System.Web.Http.Validation.ModelValidatorCache.GetValidators(ModelMetadata metadata)
        at System.Web.Http.Controllers.HttpActionContextExtensions.GetValidators(HttpActionContext actionContext, ModelMetadata metadata)
        at System.Web.Http.Validation.ModelValidationNode.ValidateThis(HttpActionContext actionContext, ModelValidationNode parentNode)
        at System.Web.Http.Validation.ModelValidationNode.Validate(HttpActionContext actionContext, ModelValidationNode parentNode)
        at System.Web.Http.ModelBinding.Binders.CompositeModelBinder.BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        at System.Web.Http.ModelBinding.ModelBinderParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
        at System.Web.Http.Controllers.HttpActionBinding.c__DisplayClass1.b__0(HttpParameterBinding parameterBinder)
        at System.Linq.Enumerable.WhereSelectArrayIterator
        2.MoveNext()
        at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken)

        • Mark

          I meant to say if the parameter is null there’s no issue but if not I see that exception. I’ll look into it further.

          • Filip W

            Mark, you are absolutely right. I just quickly tried that before leaving to work and indeed it behaves like you mentioned. The thing is I wrote this post against nightlies from 12 May (I believe, maybe 16th) and it worked fine.

            I’ll have a look this evening when I’m back, but if you figure this out please share the solution :)

  • Timmy

    I am having the same problem.

    I raised an issue on Codeplex – http://aspnetwebstack.codeplex.com/workitem/191
    Maybe MS can help.

  • Kamran

    Now that the RC is out, the interface has changed so GetService and GetServices are on IDependencyResolver, along with BeginScope. Any ideas now?

  • Kamran

    Actually, using your current GitHub source, it works! Thanks!

  • Brad

    In the NinjectScope code, it looks like some code got cut off. What do you have for IDependencyScope implementions of IEnumerable GetServices(Type serviceType), and Dispose() ? I couldn’t figure it out from the ninject api.

  • Stephen M. Redd

    I’ve migrated one of my projects up to the RC, and I’m using this NinjectResolver and NinjectScope.

    The problem I’m running into now is that binding that are specified as InRequestScope aren’t working. I’m getting a new instance of these every time. With WebApi, via regular MVC 4 RC controllers InRequestScope works fine. This same code was also working fine with WebApi from the beta version.

    Anyone have any ideas how to get InRequestScope functionality back with the new dependency resolver design?

    • Stephen M. Redd

      Ok, I mostly figured it out. The problem is that the NinjectResolver’s BeginScope method returns a new NinjectScope passing in _kernel.BeginBlock() to the constructor. This re-associates the instances from the scope that was declared in their bindings to the new activation block.

      So, what was once scoped to the request is now scoped to the activation block.

      To work around this I changed NinjectResolver like this:

      public IDependencyScope BeginScope()
      {
      return new NinjectScope(_kernel);
      }

      Then, to keep the NinjectScope from disposing of the kernel itself, I commented out everything in the NinjectScope’s Dispose block.

      As far as I can tell, this should not have much impact. The built-in cache-and-collect mechanism will work as it typically does, polling the GC and disposing instances in the cache automatically.

      I think the core of the problem here is that WebApi itself is trying to do the job of per-request scope management, but Ninject already has a way of doing that job. The two different scoping mechanisms don’t seem to live in harmony.

  • Brian

    Hey, this works great for my ApiControllers, thanks!

    However, I have a custom AuthorizeAttribute which I was previously doing property injection on and it worked fine, but now it doesn’t seem to be getting injected (though, if I add the same type to a controller, it works).

    Any ideas?

    Thanks!

    • Weston

      Any luck with this. I have been scouring the web to try and figure out how to inject the web-api filters with Ninject.

      • Weston

        Not quite DI. More like the service locator antipattern, but temporarily I am using GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(MyService)) to compensate for my lack of filter support under the webapi.

  • http://paigecsharp.blogspot.com Paige COok

    Thanks for the great work on figuring this out. Since the RC is now out and available, I forked the Ninject.Web.WebApi project on Github and incorporated your code changes as well as the comments here on the site and submitted a pull request – https://github.com/ninject/Ninject.Web.WebApi/pull/2 to get this incorporated into the project. Hope that you do not mind…

    • Filip W

      Absolutely not! As I wrote, I based that on Mike Wasson’s Unity example from asp.net :)

  • Fred

    Injection works fine in web api controllers with MVC 4 RC. But I have the same problem that Brian : properties in my custom AuthorizeAttribute are not injected.
    How to do it ?

  • key2

    It’s a nice post.

  • Christian

    Thanks!

  • Jacob Eisenberg

    Suggested fix for per-request not working:
    https://gist.github.com/2947483

  • Pingback: Notebook Heavy » Setup StructureMap with ASP.NET Web API Release Candidate » Notebook Heavy

  • SMK

    Hi,

    I am still getting err could not resolve “GlobalConfiguration.Configuration.DependencyResolver” any thoughts? I upgraded from MVC4 beta to RC via nuget.

    • Filip W

      Looks like Beta is still in your GAC. Uninstall Beta from the Control Panel > Programs and install MVC4 RC from http://www.asp.net/web-api

  • Nick

    Hi Filip, I am using mvc 4 rc and implemented your approach for ninject. my constructor injection works great, however i do have a property injection in one of my custom authorization filter which is not working. ( was working with beta thought). trying to figure out the issue, any expert opinion would be very much helpful.

  • xvn

    Thanks so much!

  • Andrew Webb

    What you have on GitHub (dating from end May 2012) works great with ASP.NET Web API RTM. Great work! Thanks.

  • Pingback: Ninject对Web Api的支持问题 | 编程·早晨

  • http://www.yassershaikh.com Yasser Shaikh

    Hey, I got my web api working with ninject, thanks to this article and the nuget extension…

    BUT…

    can you please explain why this is needed, I have failed to understand why this change is required.

    Thanks in advance

    -Yasser

  • Fred

    Filip, I’m, using WEBAPI & Standard controllers in the same project after implementing your code, I get this error for the standard homecontroller.

    “No parameterless constructor defined for this object. ”

    Any help would be appreciated.

    TIA
    Fred

    • Filip W

      Hi

      This resolver is only for Web API, the MVC controllers needs a regular MVC Ninject resolver (you can grab one off nuget just look for “ninject.mvc3″). Unfortunately MVC Controllers and Web API Controllers are two completely different animals.

      /filip

  • Todd

    Great article! I have everything wired up and able to now hit my URL. The session gets created and appears to be configured properly but the query never gets executed.

    private static IKernel CreateKernel()
    {
    var kernel = new StandardKernel();
    kernel.Bind<Func>().ToMethod(ctx => () => new Bootstrapper().Kernel);
    kernel.Bind().To();

    // Tenant
    kernel.Bind().To().InSingletonScope();
    kernel.Bind().ToMethod(c => c.Kernel.Get().CreateSession());
    kernel.Bind().To();

    RegisterServices(kernel);

    // Set Web API Resolver
    GlobalConfiguration.Configuration.DependencyResolver = new eReferralPro.NHibernate.NinjectResolver(kernel);

    return kernel;
    }

    private readonly ISession _session;

    public ProductController(ISession session)
    {
    _session = session;
    }

    public IEnumerable GetAllProducts()
    {
    List products = _session.CreateCriteria().List();

    return products;
    }

  • http://www.facebook.com/erik.hennerfors Erik Hennerfors

    Quite lovely sulotion!

  • Pingback: Using Ninject – Dependency Injection with ASP.NET Web API controllers | | Yasser ShaikhYasser Shaikh

  • Pingback: ASP.NET Web API In-Memory Hosting Integration Test and Ninject | Notebook Heavy

  • Robert Wilson

    Ahhh! I have been trying to solve this for weeks. I’ve followed some other articles on this topic, but wasn’t able to get any of the other solutions to work (some seemed outdated, Feb 2012 and earlier).

    Great article. Worked like a charm for me.

    • http://www.strathweb.com/ Filip W

      thanks!

  • Pingback: Building an ASP.NET MVC4 Application with EF and WebAPI | Shubh Dasgupta: Web Developer and Designer

  • RonnieOverby

    Thanks for this post, it really helped me. It’s just too bad that I had to swim through an ocean of irrelevant information to get here.

  • Fedor

    Thanks! Got here from StackOverflow after failing to find an answer there.

  • Pingback: Setting Up Ninject with Asp.net web api