Question

We have a (weird?) requirement to create X number of instances of the same class and we are using Ninject for IoC. Obviously we can do this manually by requesting objects of the same type multiple times:

for (var i = 0; i <= 10; i++)
{
    var obj = _kernel.GetService(typeof(MyType));
    ...
}

This works as expected, but right now we have to access the kernel in our code to do this and that 'smells'. I'd prefer to handle this in the binding, sort of like the InRequestScope stuff.

To add a bit of complexity to all this we also specify the number of instances on the object it self, so the code above is actually closer to this: var obj = (MyType)_kernel.GetService(typeof(MyType));

for (var i = 0; i <= obj.NumberOfInstances; i++)
{
    var obj = _kernel.GetService(typeof(MyType));
    ...
}

And one final thing to really make it messy; we use Ninject Conventions to bind the implementations to a base: var assemblies = AppDomain.CurrentDomain.GetAssemblies();

this.Bind(x => x
      .From(assemblies)
      .SelectAllClasses()
      .InheritedFrom<BaseMyType>()
      .BindAllBaseClasses());

The implementation I would love to be able to use is this:

this.Bind(x => x
      .From(assemblies)
      .SelectAllClasses()
      .InheritedFrom<BaseMyType>()
      .BindAllBaseClasses()
        .Configure(syntax => syntax.InMultipleScope()));

But this may or may not be possible or a good idea...

Has anybody done anything like this? Or perhaps you can see a different way of doing it?

Details on requirement: We create multiple threads in a single Azure Worker Role and it would be beneficial to run multiple copies of the same code.

Was it helpful?

Solution

You asked for it, so i'm going to give you this really smelly, ugly code ;-)

using System;
using System.Collections.Generic;
using FluentAssertions;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Extensions.Conventions.BindingGenerators;
using Ninject.Syntax;
using Xunit;

public class MultiBaseBindingGenerator : IBindingGenerator
{
    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
    {
        for (int i = 0; i < 10; i++)
        {
            yield return bindingRoot.Bind(type.BaseType).To(type);
        }
    }
}

public abstract class Foo
{
}

public class SomeFoo : Foo
{
}

public class SomeFooUser
{
    public IEnumerable<Foo> Foos { get; set; }

    public SomeFooUser(IEnumerable<Foo> foos)
    {
        this.Foos = foos;
    }
}

public class Demo
{
    [Fact]
    public void FactMethodName()
    {
        var kernel = new StandardKernel();

        kernel.Bind(x => x.FromThisAssembly()
            .SelectAllClasses()
            .InheritedFrom<Foo>()
            .BindWith<MultiBaseBindingGenerator>());

        kernel.Get<SomeFooUser>().Foos.Should().HaveCount(10);
    }
}

This will create 10 instances of Foo every time you inject an IEnumerable<Foo>. You could make the instance count configurable by putting an attribute on the type and reading it @ IBindingGenerator. However, configuration can only be read when or before creating the bindings.


There are several other possibilities to reach the same goal. I would suggest creating only one binding - as you are doing now. Create a factory `IEnumerable CreateMultipleAsConfigured()' which creates as many instances as required.


Another possibility would be to create your own collection type:

public class MultipleAsConfigured<T> : Collection<T> { }

and bind it using a provider:

IBindingRoot.Bind(typeof(MultipleAsConfigured<>).ToProvider(typeof(MultipleAsConfiguredProvider));   

and have the provider read the configuration, instanciate as many objects as required (using IContext.Get()), creating the MultipleAsConfigured, and adding the items...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top