I have the problem with implementing of generic controller for CRUD operations with my entities.
Currently I have the following classes:
public class BaseItem
{
public int? Id { get; set; }
}
This class is parent for multiple entities:
public class Customer : BaseItem
{
// some fields
}
public class Project : BaseItem
{
// some fields
}
Then there is interface for generic repository:
public interface IGenericItemRepository<TDomain>
where TDomain : BaseItem
{
// methods
}
And based on this interface are more specific interfaces:
public interface ICustomerRepository : IGenericItemRepository<Customer> {}
In order not to solve EF now, I use mock classes. They are done as the following:
public abstract class GenericMockRepository<TDomain> : IGenericItemRepository<TDomain> where TDomain : BaseItem
...
and it implements all the logic for basic CRUD. Specific mock repository is done like this:
public class MockCustomerRepository : GenericMockRepository<Customer>, ICustomerRepository
{
public MockCustomerRepository() : base()
{
CreateCustomersList();
}
private void CreateCustomersList()
{
// method body
}
}
So overall I could use my existing setup for implementing controller in this way:
public class CustomersController : Controller
{
private ICustomerRepository _customerRepo;
public CustomersController(ICustomerRepository customerRepo)
{
_customerRepo = customerRepo;
}
}
Then I've tried to implement generic controller:
public interface IBaseController<TDomain, TDetailsViewModel, TEditViewModel, TRepo>
where TDomain : BaseItem
where TDetailsViewModel : BaseViewModel, new()
where TEditViewModel : BaseViewModel, new()
where TRepo : IGenericItemRepository<BaseItem>
{
[HttpGet] public IActionResult Edit(int? id);
[HttpPost] public IActionResult Edit(TDomain entity);
[HttpGet] public IActionResult Details(int? id);
}
public class BaseController<TDomain, TDetailsViewModel, TEditViewModel, TRepo> : Controller, IBaseController<TDomain, TDetailsViewModel, TEditViewModel, TRepo>
where TDomain : BaseItem
where TDetailsViewModel : BaseViewModel, new()
where TEditViewModel : BaseViewModel, new()
where TRepo : IGenericItemRepository<BaseItem>
{
private IGenericItemRepository<BaseItem> _repo;
public BaseController(IGenericItemRepository<BaseItem> repo)
{
_repo = repo;
}
}
But when I try to change my CustomerController to this code below:
public class CustomersController : BaseController<Customer, BaseViewModel, BaseViewModel, ICustomerRepository>
{
private ICustomerRepository _customerRepo;
public CustomersController(ICustomerRepository customerRepo)
{
_customerRepo = customerRepo;
}
}
there is the following error:
Error CS0311 The type 'DAL.Repositories.ICustomerRepository' cannot be used as type parameter 'TRepo' in the generic type or method 'BaseController<TDomain, TDetailsViewModel, TEditViewModel, TRepo>'. There is no implicit reference conversion from 'DAL.Repositories.ICustomerRepository' to 'DAL.Repositories.IGenericItemRepository<Domain.BaseItem>'.
But my ICustomerRepository is type of IGenericItemRepository, isn't it?
How could I fix the error and properly implement generic controller please, so I could reuse it in my specific controllers? I was checking similar topics, but as there are implemented generic repository in another way than I have, I could not fix it based on the examples... The reason why I have additional interfaces for specific repositories is that for each entity type there will be more specific methods, so I'd like to define them in specific interfaces then (so that's why I don't have just something like
public interface IBaseRepository
{
T Get<T>(int id);
void Save<T>(T entity);
}
Thanks a lot for any help in advance!
ICustomerRepositoryis type ofIGenericItemRepository, isn't it? no, look at Covariance and contravariance in generics