Question

I would like to be able to use ninject to inject all instances of a particular generic type into a class. For example I have a bunch of custom extractors of a format similar to:

public interface IExtract<TEntity> 
{ 
    TEntity ExtractFrom(MyBulkExportedEntity exportedEntity);
}

and I want to inject all instances of these extractors into a class responsible for processing this file using ninject multiple binding.

ie

public class ProcessDataExtract
{
    /*This isn't valid c# but demonstrates the intent of what i would like to do*/
    public ProcessDataExtract(IEnumerable<IExtract<>> allExtractors)
    {
    }

    public void Process(MyBulkExportedEntity exportedEntity)
    {
        /*loop through all of the extractors and pull relevant data from the object*/
    }
}

In the past i have done this by having a management class (IProvideExtractors) which accesses the kernel directly but i don't like this method and was wondering if anyone knows of a better way to do this. With ninject multiple binding I can then get all of the instances im interested in using kernel.GetAll(typeof(IExtract<>))

Was it helpful?

Solution

I was looking for something related: I don't wanted to specify all the bindings separately using the Convention extension.

First: You need to inject List<IExtract> and inherit IExtract<T> : IExtract . This is simply due to the fact, that in C# you can not specify the type of a collection containing different generics. As you noted in your question it is invalid syntax - for a good reason beyond this answer.

You can later pull the elements of IExtract out of the list and use reflection to get the generic type paramer and cast it back. Or if you know for what extractor you are looking:

public IExtract<T> GetExtractor<T>() {
    return (IExtract<T>)Extractors.Find(e => e is ExtractImpl<T>);
}

Now you might have a bunch of classes for that you want some T bound to IExtract`.

Bind<IExtract>().To<ExtractImpl<MyEntity>>();
Bind<IExtract>().To<ExtractImpl<YourEntity>>();

where

MyEntity : BaseEntity
YourEntity : BaseEntity

You can specify a Convention as following

Kernel.Bind(x => x.FromThisAssembly().SelectAllClasses()
    .InheritedFrom<BaseEntity>()
    .BindWith(new GenericArgumentBindingGenerator(typeof(IExtract<>))));

Where GenericArgumentBindingGenerator is defind as:

public class GenericArgumentBindingGenerator : IBindingGenerator
{
    private readonly Type m_Generic;

    public GenericArgumentBindingGenerator(Type generic)
    {
        if (!generic.IsGenericTypeDefinition)
        {
            throw new ArgumentException("given type must be a generic type definition.", "generic");
        }
        m_Generic = generic;
    }

    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (bindingRoot == null)
            throw new ArgumentNullException("bindingRoot");
        if (type.IsAbstract || type.IsInterface)
        {
            return Enumerable.Empty<IBindingWhenInNamedWithOrOnSyntax<object>>();
        }

        var bindings = new List<IBindingWhenInNamedWithOrOnSyntax<object>>();
        IBindingWhenInNamedWithOrOnSyntax<object> binding = bindingRoot
            .Bind(typeof(IExtract)) // you maybe want to pass typeof(IExtract) to constructor
            .To(m_Generic.MakeGenericType(type));

        bindings.Add(binding);

        return bindings;
    }
}

OTHER TIPS

The answer to this seems to be that there isnt a way of doing this with ninject

Option A

I'm pretty sure you can do this:

public class ProcessDataExtract
{
    public ProcessDataExtract<TExtract>(IEnumerable<IExtract<TExtract>> allExtractors)
    {
    }
    ...etc...
}

And then list out your bindings in your binding module's Load method:

...
Bind<IExtract<TEntity>>().To<SomeConcreteExtract>();
Bind<IExtract<TEntity>>().To<AnotherConcreteExtract>();
Bind<IExtract<TEntity>>().To<YetAnotherConcreteExtract>();
...

And NInject will deliver them to your constructor that advertises a dependency on a bunch of them. I've done that in the past with success.

Option B

Change

public interface IExtract<TEntity> 
{ 
    TEntity ExtractFrom(MyBulkExportedEntity exportedEntity);
}

to

public interface IExtract
{ 
    TEntity ExtractFrom<TEntity>(MyBulkExportedEntity exportedEntity);
}

Which would allow:

        public ProcessDataExtract<TExtract>(IEnumerable<IExtract<TExtract>> allExtractors)
    {
    }
    ...etc...

to be:

    public ProcessDataExtract(IEnumerable<IExtract> allExtractors)
    {
    }
    ...etc...

And the NInject bindings would be adjusted, too:

...
Bind<IExtract>().To<SomeConcreteExtract>();
Bind<IExtract>().To<AnotherConcreteExtract>();
Bind<IExtract>().To<YetAnotherConcreteExtract>();
...
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top