Search code examples
ninject

How to support dynamic multiinjection with Ninject, where there may be NO items bound


In the following unit tests, TestDirectRetrieval_WithNoImplementations and TestInjection_WithNoImplementations fail

[TestFixture]
public class KernelTests
{
    [Test] //Passes
    public void TestDirectRetrieval_WithMultipleImplementations()
    {
        //Arrange
        var kernel = new StandardKernel();
        kernel.Bind<Foo>().ToConstant(new Foo("a"));
        kernel.Bind<Foo>().ToConstant(new Foo("b"));
        //Act + Assert
        Assert.DoesNotThrow(() => kernel.GetAll<Foo>().ToList());
    }

    [Test] //Passes
    public void TestInjection_WithMultipleImplementations()
    {
        //Arrange
        var kernel = new StandardKernel();
        kernel.Bind<Foo>().ToConstant(new Foo("a"));
        kernel.Bind<Foo>().ToConstant(new Foo("b"));
        //Act + Assert
        Assert.DoesNotThrow(() => kernel.Get<Bar>());
    }

    [Test] //Fails
    public void TestDirectRetrieval_WithNoImplementations()
    {
        //Arrange
        var kernel = new StandardKernel();
        //Act + Assert
        Assert.DoesNotThrow(() => kernel.GetAll<Foo>().ToList());
    }

    [Test] //Fails
    public void TestInjection_WithNoImplementations()
    {
        //Arrange
        var kernel = new StandardKernel();
        //Act + Assert
        Assert.DoesNotThrow(() => kernel.Get<Bar>());
    }

    #region Test helper classes

    class Foo
    {
        public Foo(string someArgThatCantBeAutomaticallyResolvedByNinject){}
    }

    class Bar
    {
        private List<Foo> myFoos;

        public Bar(IEnumerable<Foo> foos)
        {
            myFoos = foos.ToList();
        }
    }
    #endregion
}

In a scenario were someone is binding things to constants / implementations based on some dynamic situation, how can they support the case were nothing is bound at all?


Solution

  • GetAll is not used by ninject for injection, but as you found out it provides the desired behavior. To enable injection with 0-n semantics I commonly roll my own "collection", an uncomplete / pseudo code version looks like this:

    class OptionalItems<T> : IEnumerable<T> 
    {
       private readonly T[] items;
    
       public OptionalItems(IResolutionRoot resolutionRoot)
       {
           this.items = resolutionRoot.TryGetAll(T).ToArray();
       }
    
       public IEnumerator<T> GetEnumerator() {
           return this.items.GetEnumerator();
       }
    }
    

    Now inject OptionalItems instead of IEnumerable<T> or IList<T>. For testability-reasons it might make sense to create an interface for OptionalItems: public interface IOperationalItems<T> : IEnumerable<T> and bind that.