3
votes

I am creating a PersonalityConfiguration class which I want to be a Singleton, and for the most part the Singleton binding works fine. Except for when this "Singleton" is binded through its IPersonalityProvider interface in the Constructor of a Class. When that Binding is Injected, it is not the Singleton, not the same instance of PersonalityConfiguration but rather a new Instance each time. How would I bind the same Singleton to the dependent classes through the IPersonalityProvider interface?

I have the following Inteface and Class Definitions

Interface IPersonalityProvider

interface IPersonalityProvider
{
    void BeHappy();
}

Class PersonalityConfiguration

class PersonalityConfiguration : IPersonalityProvider
{
    private int m_personalityScale = 0;
    public PersonalityConfiguration()
    {
        m_personalityScale = 0;
    }

    public void BeHappy()
    {
        throw new NotImplementedException();
    }
}

Class Person

class Person
{
    public Person(IPersonalityProvider personality)
    {
        Personality = personality;
    }

    public IPersonalityProvider Personality { get; set; }
}

My Ninject Module is as follows

class ProgramNinjectModule : NinjectModule
{
    public override void Load()
    {
        Bind<PersonalityConfiguration>().ToSelf().InSingletonScope();
        Bind<IPersonalityProvider>().To<PersonalityConfiguration>();
    }
}

Here is my Main

   static void Main(string[] args)
    {
        using (var nKernel = new StandardKernel(new ProgramNinjectModule()))
        {

            //Singleton works here
            PersonalityConfiguration crazy1 = nKernel.Get<PersonalityConfiguration>();  
            PersonalityConfiguration crazy2 = nKernel.Get<PersonalityConfiguration>();  
            Assert.AreEqual(crazy1, crazy2); //True as Expected

            //Expecting PersonalityConfig Singleton to bind to Person1 and 2, does not
            Person person1 = nKernel.Get<Person>();  
            Person person2 = nKernel.Get<Person>();
            Assert.AreEqual(person1.Personality, person2.Personality);  //False, Bombs

            //Obviously this works
            Person person3 = new Person(crazy1);
            Person person4 = new Person(crazy1);
            Assert.AreEqual(person3.Personality, person4.Personality);  //True as Expected
        }

    }
2
You are making two bindings, which basically means two registrations in the internal dictionary. The first has a key of PersonalityConfiguration and the second has IPersonalityProvider. Ninject will simply lookup a resolution based on the key in this dictionary and those two registrations are not related in any way. What Ninject is concerned they are completely different, even though they share the same implementation type. - Steven

2 Answers

4
votes

The clean way to achieve what you want is by one single binding:

Bind<IPersonalityProvider, PersonalityConfiguration>()
    .To<PersonalityConfiguration>()
    .InSingletonScope();

this means whenever ninject is requested to return an IPersonalityProvider or an PersonalityConfiguration it will return the same (singleton) instance of PersonalityConfiguration.

I've verified that it works correctly using the following code:

using System;
using FluentAssertions;
using Ninject;
using Ninject.Modules;
using Xunit;

namespace NinjectTest.SingletonBoundToMultipleTypes
{
    interface IPersonalityProvider
    {
        void BeHappy();
    }

    class PersonalityConfiguration : IPersonalityProvider
    {
        private int m_personalityScale = 0;
        public PersonalityConfiguration()
        {
            m_personalityScale = 0;
        }

        public void BeHappy()
        {
            throw new NotImplementedException();
        }
    }

    class Person
    {
        public Person(IPersonalityProvider personality)
        {
            Personality = personality;
        }

        public IPersonalityProvider Personality { get; set; }
    }

    class ProgramNinjectModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IPersonalityProvider, PersonalityConfiguration>()
                .To<PersonalityConfiguration>()
                .InSingletonScope();
        }
    }

    public class Test
    {
        [Fact]
        public void PerformTest()
        {
            using (var nKernel = new StandardKernel(new ProgramNinjectModule()))
            {
                //Singleton works here
                PersonalityConfiguration crazy1 = nKernel.Get<PersonalityConfiguration>();
                PersonalityConfiguration crazy2 = nKernel.Get<PersonalityConfiguration>();
                ReferenceEquals(crazy1, crazy2).Should().BeTrue();

                //Expecting PersonalityConfig Singleton to bind to Person1 and 2, does not
                Person person1 = nKernel.Get<Person>();
                Person person2 = nKernel.Get<Person>();
                ReferenceEquals(person1.Personality, person2.Personality)
                    .Should().BeTrue();

                //Obviously this works
                Person person3 = new Person(crazy1);
                Person person4 = new Person(crazy1);
                ReferenceEquals(person3.Personality, person4.Personality)
                    .Should().BeTrue();
            }
        }
    }
}
1
votes

Looks like I used the following binding and it worked

class ProgramNinjectModule : NinjectModule
{
    public override void Load()
    {
        Bind<PersonalityConfiguration>().ToSelf().InSingletonScope();
        Bind<IPersonalityProvider>().ToMethod(n => n.Kernel.Get<PersonalityConfiguration>()).WhenInjectedInto<Person>();
    }
}