Gibsnag
Gibsnag

Reputation: 1087

Ninject Property Injection on base class

I'm attempting to inject a property using ninject. Given the two bindings in the ninject module below, I would expect the ConcreteDependency to be injected into B.
However, it seems that WhenInjectedInto doesn't consider the type being injected into, just the declaring type of the target (property in this case).

Is there a way to achieve the behaviour I expected?

static void Main(string[] args)
{
    var kernel = new StandardKernel(new TestModule());
    var b = kernel.Get<B>();
    var c = kernel.Get<C>();
}

class TestModule : NinjectModule
{
    public override void Load()
    {
        Bind<IDependency>().To<EmptyDependency>();
        Bind<IDependency>().To<ConcreteDependency>().WhenInjectedInto<B>();
    }
}

abstract class A
{
    [Inject]
    public IDependency Dependency { get; set; }
}

class B : A {}

class C : A {}

interface IDependency {}

class EmptyDependency : IDependency { }

class ConcreteDependency : IDependency { }

Upvotes: 7

Views: 5053

Answers (2)

Necrototem
Necrototem

Reputation: 554

In order to check against a concrete type, you can use ParentContext.Plan.Type on the IRequest interface. This should give you the behaviour you expected from WhenInjectedInto. For example:

When(req => req.ParentContext.Plan.Type == typeof(B))

Upvotes: 4

Ilya Ivanov
Ilya Ivanov

Reputation: 23646

You should use constructor injection instead of property injection if possible. This is a better technique, which is recommended by Mark Seeman, because makes dependencies required for object construction explicit and object signature via constructor is more expressive. Code should look like this:

    abstract class A
    {
        public IDependency Dependency { get; private set; }

        public A (IDependency dependency)
        {
            Dependency = dependency;
        }

    }

    class B : A
    {
        public B (IDependency dependency)
            : base(dependency)
        {

        }
    }

    class C : A
    {
        public C (IDependency dependency)
            : base(dependency)
        {

        }
    }

    interface IDependency { }

    class EmptyDependency : IDependency { }

    class ConcreteDependency : IDependency { }

Configuration will be the same as in you example. The following test passes

    [Test]
    public void TestSpecificBindingToObjectB()
    {
        var kernel = new StandardKernel(new TestModule());
        var b = kernel.Get<B>();
        var c = kernel.Get<C>();

        Assert.AreNotEqual(b.Dependency.GetType(), c.Dependency.GetType());
        Assert.AreEqual(typeof(ConcreteDependency), b.Dependency.GetType());
    }

If you have an optional dependency with default implementation and you are ok with decorating your classes with Inject attribute, you can can pull parent information from request, like this:

class TestModule : NinjectModule
{
    public override void Load()
    {
        Bind<IDependency>().To<EmptyDependency>();
        Bind<IDependency>().To<ConcreteDependency>().When(req =>req.ParentContext.Request.Service == typeof(B));
    }
}

Then the same test given above passes for your class hierarchy with property injection.

Upvotes: 8

Related Questions