Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject Injection of all instances of a generic type with ninject

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<>))

like image 865
Not loved Avatar asked Jun 07 '12 20:06

Not loved


People also ask

What is ninject dependency injection?

Ninject is a lightweight dependency injection framework for . NET applications. It helps you split your application into a collection of loosely-coupled, highly-cohesive pieces, and then glue them back together in a flexible manner.

Is Ninject free?

Ninject is and will always be free for both personal and commercial projects. It's also open source, so you can fork the code and make any changes you like.


3 Answers

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;
    }
}
like image 114
Tarion Avatar answered Oct 06 '22 11:10

Tarion


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

like image 40
Not loved Avatar answered Oct 06 '22 12:10

Not loved


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>();
...
like image 35
bluevector Avatar answered Oct 06 '22 13:10

bluevector