How to bind Generic-type interfaces in Ninject
Asked Answered
S

1

23

I'm fairly new to Ninject, and found myself stumbling when I came to implement a generic repository pattern. I want to bind a dependency IRepository<IEntityType> to a class ConcreteRepository<EntityType> where ConcreteRepository<T> implements IRepository<T> and EntityType implements IEntityType. I tried this:

kernel.Bind<IRepository<IEntityType>>().To<ConcreteRepository<EntityType>>();

...but Ninject won't take that because it doesn't know or care that EntityType implements IEntityType. How can I go about binding this dependency?

UPDATE

This is the error I'm getting:

Error 3 The type 'ICM.Dependency.Repository.ConcreteRepository' cannot be used as type parameter 'TImplementation' in the generic type or method 'Ninject.Syntax.IBindingToSyntax.To()'. There is no implicit reference conversion from 'ConcreteRepository<EntityType>' to 'IRepository<IEntityType>'.

SOLUTION

I still don't quite understand why my binding doesn't work, but evidently I was using generics incorrectly there. As such the solution doesn't really relate to NInject. I ended specifying the ConcreteRepository to explicitly connect IEntityType with TEntityType:

public class ConcreteRepository<TInterface, TEntity> : IRepository<TInterface> where TEntity : TInterface { ... }

Then the injection can be written as follows:

kernel.Bind<IRepository<IEntityType>>().To<ConcreteRepository<IEntityType,EntityType>>()
Sequential answered 20/4, 2012 at 9:33 Comment(7)
What message are you getting? Is it a compile one or runtime? Does ConcreteRepository<EntityType> implement IRepository<IEntityType> (Also, generally one would have the ctor dependency be on on IRepository<EntityType> - in which case your Bind above would be wrong.Littlest
@RubenBartelink It's a compile error; see update above. To your first point- it does not, but as mentioned ConcreteRepository<T> implements IRepository<T>. Maybe that's my issue. To your second point, I want to use IRepository<IEntityType> in the constructor to allow for flexibility-- the repository dependency should deal with an interface, not concrete entities.Sequential
Right. Well the where constraint that's causing the compile error is there for a reason - there simply is no way in which the concrete component type you're suggesting is going to be convertible to the service type you're Binding to so stop looking! I suggest looking at how others have implmented generic repository patterns - you're far away from normal practice. You could explain what you're getting from this stuff in your question here, but I'd suggest you should ask yourself first and then maybe here whether your class hierarchy can be rationalised.Littlest
@RubenBartelink That's as may be, but my question isn't how to implement a generic repository. It's how to bind a dependency to a generic interface.Sequential
@dbaseman why are you wanting your concrete repository to take a concrete entity at all? couldn't it just take your entity interface?Tanah
Sorry for confusing matters by mixing opinion with what's needed for your question to make sense. I will concentrate on teh lattter now:- You've shown a compiler error; the compiler error makes sense - what you're asking doesnt make sense and could never work - be thankful its a compile time rather than runtime issue. Can you show excerpts of the resoluton target (i.e. where the injection is going into, and the entity and repository interfaces. As it stands, @Luke McGregor hasnt been able to answer you and I for one havent any idea what you're trying to achieve - its not like anything else.Littlest
@LukeMcGregor No, it has to bind to a concrete entity, because it binds to a data access class that needs to instantiate actual objects.Sequential
T
47
kernel.Bind(typeof(IRepository<>)).To(typeof(SimpleRepository<>));

Take a look at my one if you want here: http://blog.staticvoid.co.nz/2011/10/staticvoid-repository-pattern-nuget.html i have binding examples

EDIT:

The error you are getting is saying that your concrete repository isnt an instance of the generic one you want to bind to, ie you will need to do this

public class ConcreteRepository<ConcreteEntity> : IRepository<IEntity>{}

not

public class ConcreteRepository<ConcreteEntity> : IRepository<ConcreteEntity>{}
Tanah answered 20/4, 2012 at 9:37 Comment(3)
@RubenBartelink hmm i see what you mean, the issue is that the above scenario will work for binding IRepository<IEntity> to ConcreteRepository<IEntity> but not to ConcreteRepository<ConcreteEntity>. I'm not sure that there would be a way of producing this binding for all types in Ninject at all. To get this effect you would need to manually bind each repository.Tanah
Apologies for the confusion-- I was trying to simplify my use case by abstracting it, but I think that just obfuscated what I was trying to do. I basically implemented what you are suggesting; only I added a second type parameter to preserve the interface to the entity in the class definition.Sequential
@dbaseman i actually quite like that, it means your concrete repository is a map between an abstract construct and a concrete oneTanah

© 2022 - 2024 — McMap. All rights reserved.