#c# #asp.net-core #generics #interface #repository-pattern
#c# #asp.net-ядро #универсальные #интерфейс #репозиторий-шаблон
Вопрос:
У меня проблема с реализацией универсального контроллера для операций CRUD с моими объектами.
В настоящее время у меня есть следующие классы:
public class BaseItem
{
public int? Id { get; set; }
}
Этот класс является родительским для нескольких объектов:
public class Customer : BaseItem
{
// some fields
}
public class Project : BaseItem
{
// some fields
}
Затем есть интерфейс для общего репозитория:
public interface IGenericItemRepository<TDomain>
where TDomain : BaseItem
{
// methods
}
И на основе этого интерфейса созданы более конкретные интерфейсы:
public interface ICustomerRepository : IGenericItemRepository<Customer> {}
Чтобы не решать EF сейчас, я использую фиктивные классы. Они выполняются следующим образом:
public abstract class GenericMockRepository<TDomain> : IGenericItemRepository<TDomain> where TDomain : BaseItem
...
и он реализует всю логику для базового CRUD.
Конкретный макет репозитория выполняется следующим образом:
public class MockCustomerRepository : GenericMockRepository<Customer>, ICustomerRepository
{
public MockCustomerRepository() : base()
{
CreateCustomersList();
}
private void CreateCustomersList()
{
// method body
}
}
Таким образом, в целом я мог бы использовать свою существующую настройку для реализации контроллера таким образом:
public class CustomersController : Controller
{
private ICustomerRepository _customerRepo;
public CustomersController(ICustomerRepository customerRepo)
{
_customerRepo = customerRepo;
}
}
Затем я попытался реализовать универсальный контроллер:
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;
}
}
Но когда я пытаюсь изменить свой CustomerController на этот код ниже:
public class CustomersController : BaseController<Customer, BaseViewModel, BaseViewModel, ICustomerRepository>
{
private ICustomerRepository _customerRepo;
public CustomersController(ICustomerRepository customerRepo)
{
_customerRepo = customerRepo;
}
}
возникает следующая ошибка:
Ошибка CS0311 типа ‘DAL.Repositories.ICustomerRepository’ не может использоваться в качестве параметра типа ‘TRepo’ в универсальном типе или методе ‘BaseController<TDomain, TDetailsViewModel, TEditViewModel, TRepo>’. Нет никакого неявного преобразования ссылок из ‘DAL.Repositories.ICustomerRepository’ to ‘DAL.Repositories.IGenericItemRepository<Домен.BaseItem>’.
Но мой ICustomerRepository
— это тип IGenericItemRepository
, не так ли?
Как я могу исправить ошибку и правильно реализовать универсальный контроллер, пожалуйста, чтобы я мог повторно использовать его в своих конкретных контроллерах? Я проверял похожие темы, но поскольку существует реализованный универсальный репозиторий по-другому, чем у меня, я не смог исправить это на основе примеров… Причина, по которой у меня есть дополнительные интерфейсы для конкретных репозиториев, заключается в том, что для каждого типа сущности будут более конкретные методы, поэтому я хотел бы определить их в конкретных интерфейсах (поэтому у меня нет просто чего-то вроде
public interface IBaseRepository
{
T Get<T>(int id);
void Save<T>(T entity);
}
Большое спасибо за любую помощь заранее!
Комментарии:
1. Но мой
ICustomerRepository
типIGenericItemRepository
, не так ли? нет, посмотрите на ковариацию и контравариантность в общих чертах
Ответ №1:
Попробуйте IGenericItemRepository<TDomain>
вместо IGenericItemRepository<BaseItem>
этого в вашем BaseController
и 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);
}