Ninject InSingletonScope with Api RC Web

I'm having difficulty using the Ninject InSingletonScope binding with Api RC Web. Regardless of how I create the binding, it looks like Web Api handles the area / lifetime instead of Ninject.

I tried several options for connecting Ninject. The most common one is that here's the answer: Associating ASP.NET web APIs with ninject

I also tried this version: http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/

In both cases, I literally create the Web Api project out of the box, and then add the Ninject packages, as described in any post. Finally, I add the Resolver and Scope classes, for example, for the StackOverflow version:

public class NinjectDependencyScope : IDependencyScope { private IResolutionRoot resolver; internal NinjectDependencyScope(IResolutionRoot resolver) { Contract.Assert(resolver != null); this.resolver = resolver; } public void Dispose() { IDisposable disposable = resolver as IDisposable; if (disposable != null) disposable.Dispose(); resolver = null; } public object GetService(Type serviceType) { if (resolver == null) throw new ObjectDisposedException("this", "This scope has already been disposed"); return resolver.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { if (resolver == null) throw new ObjectDisposedException("this", "This scope has already been disposed"); return resolver.GetAll(serviceType); } } 

and

  public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver(IKernel kernel) : base(kernel) { this.kernel = kernel; } public IDependencyScope BeginScope() { return new NinjectDependencyScope(kernel.BeginBlock()); } } 

Then NinjectWebCommon looks like this:

 using System.Web.Http; using MvcApplication2.Controllers; [assembly: WebActivator.PreApplicationStartMethod(typeof(MvcApplication2.App_Start.NinjectWebCommon), "Start")] [assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(MvcApplication2.App_Start.NinjectWebCommon), "Stop")] namespace MvcApplication2.App_Start { using System; using System.Web; using Microsoft.Web.Infrastructure.DynamicModuleHelper; using Ninject; using Ninject.Web.Common; public static class NinjectWebCommon { private static readonly Bootstrapper bootstrapper = new Bootstrapper(); /// <summary> /// Starts the application /// </summary> public static void Start() { DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule)); DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule)); bootstrapper.Initialize(CreateKernel); } /// <summary> /// Stops the application. /// </summary> public static void Stop() { bootstrapper.ShutDown(); } /// <summary> /// Creates the kernel that will manage your application. /// </summary> /// <returns>The created kernel.</returns> private static IKernel CreateKernel() { var kernel = new StandardKernel(); kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel); kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>(); // Register Dependencies RegisterServices(kernel); // Set Web API Resolver GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel); return kernel; } /// <summary> /// Load your modules or register your services here! /// </summary> /// <param name="kernel">The kernel.</param> private static void RegisterServices(IKernel kernel) { kernel.Bind<ILogger>().To<Logger>().InSingletonScope(); } } } 

The ILogger and Logger objects do nothing, but illustrate the problem. Logger does Debug.Writeline so that I can see when it was created. And every page update shows that it is updated per call, not the singleton I was hoping for. Here is the controller using Logger:

 public class ValuesController : ApiController { private readonly ILogger _logger; public ValuesController(ILogger logger) { _logger = logger; _logger.Log("Logger created at " + System.DateTime.Now.ToLongTimeString()); } // GET api/values public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // GET api/values/5 public string Get(int id) { return "value"; } // POST api/values public void Post(string value) { } // PUT api/values/5 public void Put(int id, string value) { } // DELETE api/values/5 public void Delete(int id) { } } 

When I add trace information to the kernel creation, it seems that the kernel is created only once. So ... what can't I see? Why is the singlet not saved?

+17
asp.net-web-api ninject
Jul 06 '12 at 6:21
source share
3 answers

use

 public IDependencyScope BeginScope() { return new NinjectDependencyScope(kernel); } 

and do not place the kernel in a NinjectDependencyScope

+41
Jul 06 2018-12-12T00:
source share

@Remo Gloor When I run my code in InMemoryHost from WebAPI and run the integration tests, everything works fine and I have singleton. If I run the WebAPI solution inside the VS Cassini web server , the first launch will be successful and when I click update, I get an exception: Error loading Ninject ICache component This component was not registered in the kernel component container.

If I return the old code with BeginBlock , it works in Cassini, but IsSingleton no longer works in integration tests.

0
Sep 10 '12 at 13:12
source share

Instead of not deleting the kernel (which will not cause an internal order), you can simply implement your own singleton:

 public static class NinjectSingletonExtension { public static CustomSingletonKernelModel<T> SingletonBind<T>(this IKernel i_KernelInstance) { return new CustomSingletonKernelModel<T>(i_KernelInstance); } } public class CustomSingletonKernelModel<T> { private const string k_ConstantInjectionName = "Implementation"; private readonly IKernel _kernel; private T _concreteInstance; public CustomSingletonKernelModel(IKernel i_KernelInstance) { this._kernel = i_KernelInstance; } public IBindingInNamedWithOrOnSyntax<T> To<TImplement>(TImplement i_Constant = null) where TImplement : class, T { _kernel.Bind<T>().To<TImplement>().Named(k_ConstantInjectionName); var toReturn = _kernel.Bind<T>().ToMethod(x => { if (i_Constant != null) { return i_Constant; } if (_concreteInstance == null) { _concreteInstance = _kernel.Get<T>(k_ConstantInjectionName); } return _concreteInstance; }).When(x => true); return toReturn; } } 

And then just use:

 i_Kernel.SingletonBind<T>().To<TImplement>(); 

Instead of this

 i_Kernel.Bind<T>().To<TImplement>().InSingletonScope(); 


note: although these are only questions for the first request, this implementation is not thread safe.

0
Apr 03 '14 at 14:18
source share



All Articles