1

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!

1

1 Answer 1

2

Try IGenericItemRepository<TDomain> instead of IGenericItemRepository<BaseItem> in your BaseController and IBaseController.

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<TDomain>
{
    private IGenericItemRepository<TDomain> _repo;

    public BaseController(IGenericItemRepository<TDomain> repo)
    {
        _repo = repo;
    }
}

 public interface IBaseController<TDomain, TDetailsViewModel, TEditViewModel, TRepo>
    where TDomain           :   BaseItem
    where TDetailsViewModel :   BaseViewModel, new()
    where TEditViewModel    :   BaseViewModel, new()
    where TRepo             :   IGenericItemRepository<TDomain>
{
    [HttpGet] public IActionResult Edit(int? id);
    [HttpPost] public IActionResult Edit(TDomain entity);
    [HttpGet] public IActionResult Details(int? id);
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.