Ninject Injects all instances of a generic type with ninject

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

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

and I want to add all instances of these extractors to the class responsible for processing this file using multiple ninject bindings.

t

 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 did this with a management class (IProvideExtractors) that directly accesses the kernel, but I don’t like this method, and I was wondering if anyone knew of how best to do this. With multiple ninject bindings, I can get all instances interested in using kernel.GetAll(typeof(IExtract<>))

+8
c # ninject constructor-injection
source share
3 answers

I was looking for something related: I did not want to specify all the bindings separately, using the extension of the Convention.

First: You need to enter a List<IExtract> and inherit IExtract<T> : IExtract . This is because in C # you cannot specify the type of collection containing different generics. As you noted in your question, this is not valid syntax - for good reason other than this answer.

You can then infer elements from IExtract from the list and use reflection to get the type parameter and drop it. Or, if you know which extractor you are looking for:

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

Now you can have many classes for which you want some T to bind to IExtract`.

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

Where

 MyEntity : BaseEntity YourEntity : BaseEntity 

You can specify the Convention as follows

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

Where the GenericArgumentBindingGenerator displayed 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; } } 
+4
source share

The answer to this question is that there is no way to do this with ninject

+2
source share

Option A

I am sure you can do this:

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

And then list your bindings in the Load binding module:

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

And NInject will pass them on to your constructor who advertises their group dependency. I have done this in the past with success.

Option B

Edit

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

to

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

That will allow:

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

:

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

And NInject bindings will also be adjusted:

 ... Bind<IExtract>().To<SomeConcreteExtract>(); Bind<IExtract>().To<AnotherConcreteExtract>(); Bind<IExtract>().To<YetAnotherConcreteExtract>(); ... 
+1
source share

All Articles