Singleton Scope binding not working as intended
Asked Answered
F

4

9

I am using the ninject mvc3 plugin with my web api application. I have a binding that looks like:

kernel.Bind<IFoo>().To<Foo>().InSingletonScope();

It is my interpretation that the kernal will create exactly one instance of Foo and reuse it appropriately. By putting a breakpoint in Foo's constructor, I can clearly see that it is getting called once per request, and I cannot explain why.

My only guess is that somehow a new kernel is getting created per request, but that doesn't appear to be the case, as the CreateKernel method which sets the global dependency resolver is only getting run once in the application lifetime.

I am using some code taken from this post to make ninject play nice with mvc 4. Because of framework changes, I had to make an additional wrapper that I assign to GlobalConfiguration.Configuration.DependencyResolver:

public class NinjectResolver : NinjectScope, IDependencyResolver
{
    private readonly IKernel _kernel;
    public NinjectResolver(IKernel kernel)
        : base(kernel)
    {
        _kernel = kernel;
    }
    public IDependencyScope BeginScope()
    {
        return new NinjectScope(_kernel.BeginBlock());
    }
}

What am I doing wrong?

Fed answered 8/10, 2012 at 15:8 Comment(1)
Perhaps not related to captncraig's issue, but relevant to the title. I had the same problem and it turned out I accidentally was injecting the implementation into one of my objects rather than the interface. So in the constructor I had accidentally put Foo instead of IFoo. Hope this helps someone out there...Melia
F
6

I never could get it to work properly, and I am not sure why. My guess is it has something to do with MVC4 integration being a bit immature at the moment.

As an alternative I am using:

kernel.Bind<IFoo>().ToConstant(new Foo());

This seems to work, but I am not too happy with it.

Fed answered 12/10, 2012 at 22:11 Comment(1)
#11357364Materialist
S
4

Like previously mentioned it does look like a bug.
One option is to simply implement a singleton extension method yourself:

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 static object padlock = new Object();

    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)
                       {
                       lock (padlock)
                       {
                        if (_concreteInstance == null)
                        {
                            _concreteInstance = _kernel.Get<T>(k_ConstantInjectionName);
                        }
                           }
                       }


                                           return _concreteInstance;
                                       }).When(x => true);

        return toReturn;
    }
}

And then simply use:

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

Rather then

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


Skean answered 3/4, 2014 at 14:18 Comment(0)
C
4

Certainly arriving late to this thread but it just happened to me with a Windows Service hosting OWIN for Web API controllers and resolving dependencies with Ninject, InSingletonScope() wasn't working until I did the following:

var kernel = new StandardKernel();
...
kernel.Bind<Foo>().ToSelf().InSingletonScope();
kernel.Bind<IFoo>().ToMethod(context => context.Kernel.Get<Foo>());
...

// Controllers ask for the dependency as usual...
public class SomeController : ApiController
{
    readonly IFoo _foo;

    public SomeController(IFoo foo)
    {
        _foo = foo;
    }
...

Hope this helps

Crinkle answered 22/3, 2017 at 22:12 Comment(1)
thanks it solve my issue, Also with WebAPI controllers, If somebody have a clear explanation why.. I'm interested in!Candiot
M
1

Note: I used nuget to install ninject & ninject.web.mvc (which I'm sure you did as well).

I'm not able to see the rest of your code, but here's what I had in my "NinjectDependencyScope" class. (I think yours is just called NinjectScope, might be some other naming inconsistencies with your code)

public class NinjectDependencyScope : IDependencyScope
{
    private IResolutionRoot _resolver;

    internal NinjectDependencyScope(IResolutionRoot resolver)
    {
        Contract.Assert(resolver != null);

        _resolver = resolver;
    }

    #region IDependencyScope Members

    public void Dispose()
    {
        var 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);
    }

    #endregion
}

And here is my NinjectWebCommon class (located in App_Start folder):

using System;
using System.Web;
using System.Web.Http;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Modules;
using Ninject.Web.Common;

[assembly: WebActivator.PreApplicationStartMethod(typeof(ABCD.Project.Web.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(ABCD.Project.Web.App_Start.NinjectWebCommon), "Stop")]

namespace ABCD.Project.Web.App_Start
{
public static class NinjectWebCommon 
{
    private static readonly Bootstrapper Bootstrap = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        Bootstrap.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        Bootstrap.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>();

        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)
    {
        //var modules = new INinjectModule[] { new NinjectBindingModule(), };
        //kernel.Load(modules);
        Here's where you would load your modules or define your bindings manually...
    }        
}
}
Materialist answered 8/10, 2012 at 15:15 Comment(6)
My controller requires an IBar which takes an IFoo in the constructor.Fed
Bear with me while I get back up to speed on my old MVC 4 web api app that used ninject... your code sample looks somewhat familiar to what I had done...Materialist
I think maybe something odd is going on. Your code looks very similar to mine, except you are using _resolver.TryGet and I have been using _resolver.Resolve. The TryGet methods don't exist on my version of IResolutionRoot. Are you using the mvc3 plugin?Fed
I am using the Ninject.Mvc extension. From what I can see on my end, TryGet is an extension method to IResolutionRoot in Ninject DLL though. What versions of these NuGet packages are you using? I have Ninject 3.0.1.10, Ninject.Web.Common 3.0.0.7, and Ninject.MVC3 3.0.0.6Materialist
It looks like I had to add "using Ninject.Syntax" to get the TryGet extension method to work as well.Materialist
I have the same NinjectDependencyScope as captncraig and I'm still getting the same issue, did you ever get this resolved properly?Cornucopia

© 2022 - 2024 — McMap. All rights reserved.