5
votes

Using the Ninject Factory extension, you can automatically generate factories, and let the factory pass parameters to the class' constructor. The following test passes:

public interface IBar
{
    int Foo { get; }
}

public class Bar : IBar
{
    int _foo;
    public Bar(int foo) { _foo = foo; }
    public int Foo { get { return _foo; } }
}

public interface IBarFactory
{
    IBar CreateTest(int foo);
}

[Test]
public void ExecuteTest()
{
    var kernel = new StandardKernel();
    kernel.Bind<IBar>().To<Bar>();
    kernel.Bind<IBarFactory>().ToFactory();
    var factory = kernel.Get<IBarFactory>();
    var actual = factory.CreateTest(42);
    Assert.That(actual, Is.InstanceOf<Bar>());
}

However, in my particular problem, I would like it to pass the factory arguments to a dependency of the class I'm constructing, not the class itself, like this:

public interface IBarContainer
{
    IBar Bar { get; }
}

public class BarContainer : IBarContainer
{
    IBar _bar;
    public BarContainer(IBar bar) { _bar = bar; }
    public IBar Bar { get { return _bar; } }
}

public interface ITestContainerFactory
{
    IBarContainer CreateTestContainer(int foo);
}

[Test]
public void ExecuteTestContainer()
{
    var kernel = new StandardKernel();
    kernel.Bind<IBar>().To<Bar>();
    kernel.Bind<IBarContainer>().To<BarContainer>();
    kernel.Bind<ITestContainerFactory>().ToFactory();
    var factory = kernel.Get<ITestContainerFactory>();
    var actual = factory.CreateTestContainer(42);
    Assert.That(actual.Bar, Is.InstanceOf<Bar>());
}

This test fails with the message:

Ninject.ActivationException : Error activating int No matching bindings are available, and the type is not self-bindable. Activation path:
3) Injection of dependency int into parameter foo of constructor of type Bar
2) Injection of dependency IBar into parameter bar of constructor of type BarContainer
1) Request for IBarContainer

Is there a way to make the factory figure out that I want to use the passed 'foo' argument when resolving the 'Bar' dependency?

3

3 Answers

3
votes

From version 3.2.0 of Ninject.Extensions.Factory this is supported natively, using:

Ninject.Extensions.Factory.TypeMatchingArgumentInheritanceInstanceProvider

You can use it like this:

kernel.Bind<IxxxFactory>()
   .ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());

If you look at the source code, it's based on the previous answer.

4
votes

Hy, The default instance provider does not use constructor arguments which are inherited. You can change this behavior by writing your own instance provider like the following:

public class CustomInstanceProvider : StandardInstanceProvider {
    protected override ConstructorArgument[] 
        GetConstructorArguments(MethodInfo methodInfo, object[] arguments) 
        {
            var parameters = methodInfo.GetParameters();
            var constructorArguments = 
                new ConstructorArgument[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                constructorArguments[i] = 
                    new ConstructorArgument
                        (parameters[i].Name, arguments[i], true);
            }
            return constructorArguments;
        }
}

Then you only have to instruct ninject to use your own instead of the provided.

Hope that helps

1
votes

If I'm understanding your question correctly, I believe that this Ninject Wiki article has a good example ( Factory interface ) on how to use the new .ToFactory() binding extension, and how to get those generated factories to even create objects who's constructors have parameters.