Reputation: 17658
This is probably asked before but I can't work it out. Maybe if I could get the title right I could goolge it.
I have got this generic repository interface:
public interface IRepository<TEntity>
{
TEntity Resolve<TEntity>(); // dummy function, just to get the idea
}
I also have a generic unit of work, which is able to resolve a generic repository:
public interface IUnitOfWork
{
IRepository<TEntity> GetGenericRepository<TEntity>() where TEntity : class;
}
So far so good.
But as real life continues, I would like to create a custom repository, with some specific funtions. So I was thinking: inheritance; like this:
public class SpecialRepository : IRepository<SomeEntityType>
{
public void SomeSpecialFunction() { };
}
Obviously, this type cannot be resolved with the GetGenericRepository
methode so I thought: lets add a extra method to the IUnitOfWork
interface:
public interface IUnitOfWork
{
//same old get generic repository
IRepository<TEntity> GetGenericRepository<TEntity>() where TEntity : class;
//the newly added.
T GetInheretedRepository<T>() where T : class;
}
I want to be able to call the unit of work with the special repository, something like this:
public test()
{
IUnitOfWork uow = new UnitOfWork();
//I want to make this call with a constraint on TemplateRepo
//to enforce it's type: IRepository<T> (which TemplateRepo is)
var y = uow.GetInheretedRepository<TemplateRepo>();
}
The question is: how can I restrict type T
in T GetInheretedRepository<T>() where T : class;
to be of type: IRepository<TEntity>
?
I tried this:
public interface IUnitOfWork
{
//the newly added.
//error: Only class or interface could be specified as constraint
T GetInheretedRepository<T>() where T : class, IRepository; }
and
public interface IUnitOfWork
{
//the newly added.
//error: type argument missing
T GetInheretedRepository<T>() where T : class, IRepository<>;
}
that doesnt work.
I could drop the constrain as a quick-fix or perhaps create an inherited unit of work, but then; the question still remains.
Upvotes: 1
Views: 85
Reputation: 172646
The way to do this is by adding a second generic type argument, as follows:
TRepository GetInheretedRepository<TRepository, TEntity>()
where TRepository : IRepository<TEntity>
where TEntity : class;
Here you supply both the Repository type and the entity type. This way the C# compiler can check whether or not the type matches. Here's how to call it:
var rep = uow.GetInheretedRepository<SpecialRepository, SomeEntityType>();
rep.SomeSpecialFunction();
This obviously sucks, since you will have to specify both types. But more importantly, this sucks because you have to specify the concrete type, making your code take a dependency on a concrete type; a violation of the Dependency Inversion Principle.
I really would like to advice to to step away from a design where you depend on a concrete type, or even better, step away from a design where you have many methods on a specific repository class, because this violates both SRP, OCP and ISP and this will likely cause maintenance problems later on.
So instead, take a look at the application design that is described in this article.
Upvotes: 2
Reputation: 7458
You need to specify second Type like
public interface IUnitOfWork
{
//the newly added.
T GetInheretedRepository<T, TEntity>() where T : class, IRepository<TEntity>;
}
public interface IRepository<TEntity>
{
TEntity Resolve(); // dummy function, just to get the idea
}
example that compiles fine - https://dotnetfiddle.net/MmmPil
Upvotes: 1