Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject binding with WhenInjectedInto extension method

I feel I am missing something obvious. I've read several related questions on here and I've read the updated contextual bindings page on Ninject's wiki but alas it still doesn't work.

I am trying to retrofit a legacy application that used a factory pattern to use Ninject.

I have 1 interface (IInterface) implemented by 2 classes (ClassB and ClassC). IInterface has a load method. In ClassB's load method it instantiates ClassC and then executes it's load method.

Basically the program flow is ClassA creates ClassB and executes the load method. In the load method ClassB creates ClassC that does some work.

My bindings are setup as so...

Bind<IInterface>().To<ClassC>().WhenInjectedInto<ClassB>();
Bind<IInterface>().To<ClassB>().WhenInjectedInto<ClassA>();

When this runs it fails in ClassB's load method with this error...

Error activating IInterface No matching bindings are available, and the type is not self-bindable.

If I try the following...

Bind<IInterface>().To<ClassC>().WhenInjectedInto<ClassB>();
Bind<IInterface>().To<ClassB>();

It does an endless loop and never creates ClassC.

EDIT I have simplified this into a unit test that passes but doesn't give me the results I want...

[TestClass]
public class NinjectTestFixture
{
    private interface IDoSomething
    {
        void SaySomething();
    }

    private class ClassA : IDoSomething
    {
        public void SaySomething()
        {
            Console.WriteLine("Hello from Class A");
        }
    }

    private class ClassB : IDoSomething
    {
        private IKernel _Kernel;

        public ClassB(IKernel kernel)
        {
            _Kernel = kernel;
        }

        public void SaySomething()
        {
            Console.WriteLine("Hello from Class B");

            var x = _Kernel.Get<IDoSomething>();

            x.SaySomething();
        }
    }

    private class ClassC
    {
        private IKernel _Kernel;

        public ClassC(IKernel kernel)
        {
            _Kernel = kernel;
        }

        public void SaySomething()
         {
             Console.WriteLine("Hello from Class C");

             var x = _Kernel.Get<IDoSomething>();

             x.SaySomething();
         }
    }

    [TestMethod]
    public void TestMethod1()
    {
        var kernel = new StandardKernel();

        kernel.Bind<IDoSomething>().To<ClassA>();
        kernel.Bind<IDoSomething>().To<ClassB>().WhenInjectedInto<ClassC>();
        kernel.Bind<ClassC>().ToSelf();

        var x = kernel.Get<ClassC>();

        x.SaySomething();
    }

The output is: Hello from Class C Hello from Class A

But I want: Hello from Class C Hello from Class B Hello from Class A

Thanks

like image 828
oliwa Avatar asked Sep 17 '11 22:09

oliwa


1 Answers

You are not injecting into ClassC. You are passing in the kernel and resolving IDoSomething directly from it. There is a big difference.

Do not pass the kernel as a parameter--doing so is not Dependency Injection, it is Service Location (good article on the difference: Service Locator is an Anti-Pattern).

Change ClassC to be:

private class ClassC     
{         
    private IDoSomething _doSomething;          
    public ClassC(IDoSomething doSomething)
    {             
        _doSomething = doSomething;         
    }          

    public void SaySomething()          
    {              
        Console.WriteLine("Hello from Class C");               
        //var x = _Kernel.Get<IDoSomething>();               
        _doSomething.SaySomething();          
    }     
} 

You should also make the same changes to ClassA and ClassB (pass the type/interface you want resolved, not the kernel).

like image 75
Phil Sandler Avatar answered Oct 12 '22 16:10

Phil Sandler