Slavisa Radicevic
Slavisa Radicevic

Reputation: 13

Ninject - overloads for WithConstructorArgument behave weird

I have the following code:

namespace Test.Ninject.ConstructorArgumentBug
{

public class ClassA
{
    private readonly IDependecy _dependecy;

    public ClassA(IDependecy dependecy)
    {
        _dependecy = dependecy;
    }
}

public interface IDependecy
{
}

public class ConcreteDependency : IDependecy
{
    private readonly string _arg;

    public ConcreteDependency(string arg)
    {
        _arg = arg;
    }
}

public class Tests
{
    [Test]
    public void Test1()
    {
        var kernel = new StandardKernel();
        kernel.Bind<IDependecy>()
            .To<ConcreteDependency>()
            .WhenInjectedInto<ClassA>()
            .WithConstructorArgument(new ConstructorArgument("arg", "test"));

        var classA = kernel.Get<ClassA>();
        Assert.IsNotNull(classA);
    }

    [Test]
    public void Test2()
    {
        var kernel = new StandardKernel();
        kernel.Bind<IDependecy>()
            .To<ConcreteDependency>()
            .WhenInjectedInto<ClassA>()
            .WithConstructorArgument("arg", "test");

        var classA = kernel.Get<ClassA>();
        Assert.IsNotNull(classA);
    }
}
}

The Test1 doesn't work, but the Test2 does, and I believe they should behave the same. It looks like a bug to me. For some reason the first test cannot resolve dependency with the following error:

Ninject.ActivationException : Error activating string No matching bindings are available, and the type is not self-bindable. Activation path: 3) Injection of dependency string into parameter arg of constructor of type ConcreteDependency 2) Injection of dependency IDependecy into parameter dependecy of constructor of type ClassA 1) Request for ClassA

I guess if the first test fails the second should fail too. Am I doing something wrong?

Thanks!

Upvotes: 1

Views: 638

Answers (1)

Yacoub Massad
Yacoub Massad

Reputation: 27861

In Test1, the WithConstructorArgument method that you are invoking has the following signature:

IBindingWithOrOnSyntax<T> WithConstructorArgument<TValue>(TValue value);

Here is what the documentation says about this method (you get this on IntelliSense):

Indicates that the specified constructor argument should be overridden with the specified value.

And here is the documentation for TValue:

Specifies the argument type to override

And here is the documentation for value:

The value for the argument

So, this line:

.WithConstructorArgument(new ConstructorArgument("arg", "test"));

Which is equivalent to (C# automatically infers TValue):

.WithConstructorArgument<ConstructorArgument>(new ConstructorArgument("arg", "test"));

is telling NInject that the constructor of ConcreteDependency has a parameter of type ConstructorArgument, where in fact it has a parameter of type string.

So you should change it to

.WithConstructorArgument<string>("test");

Or simpler:

.WithConstructorArgument("test");

Please note that there is no overload of WithConstructorArgument that has a parameter of type ConstructorArgument.

However, there is another method called WithParameter that takes in an IParameter.

Since ConstructorArgument implements IParameter, you can use this method to specify the constructor argument via ConstructorArgument like this:

kernel.Bind<IDependecy>()
    .To<ConcreteDependency>()
    .WhenInjectedInto<ClassA>()
    .WithParameter(new ConstructorArgument("arg", "test"));

Upvotes: 2

Related Questions