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<>))
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.
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.
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;
}
}
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>();
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With