Вы можете создать базовый базовый контроллер:
[Route("[controller]")]
public abstract class BaseProductController<TProduct, TProductModel> : Controller
where TProduct : Product, new()
where TProductModel : ProductModel, new()
{
protected readonly ApplicationDbContext _context;
protected readonly IMapper _mapper;
protected BaseProductController(ApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
[HttpGet("create")]
public IActionResult Create()
{
return View(new ProductModel());
}
[HttpPost("create")]
public async Task<IActionResult> Create(TProductModel model)
{
if (!ModelState.IsValid)
return View(model);
var product = _mapper.Map<TProduct>(model);
_context.Add(product);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
// etc.
}
Затем для каждого типа продукта:
public class SomeProductController : BaseProductController<SomeProduct, SomeProductModel>
{
public SomeProductController(ApplicationDbContext context, IMapper mapper)
: base(context, mapper)
{
}
}
Примечания:
Я предполагаю, что модели представлений, потому что вы используете модели представлений, не так ли?Серьезно, однако, есть множество причин использовать модель представления, особенно для обработки сообщений.Здесь я предполагаю, что вы, вероятно, также создадите модель представления базового продукта.Вы также можете легко использовать простое ограничение class
, если хотите разрешить любой тип класса.
Для базового контроллера я использовал IMapper
из AutoMapper.Вы можете обрабатывать сопоставление другим способом, с другой библиотекой или даже вручную, если хотите.Однако абстракция, предоставляемая библиотекой сопоставлений, такой как AutoMapper, значительно упрощает эту работу.
Когда неизбежно необходимо учитывать различия, вы можете справиться с ними с помощью ловушек.Например, вы можете создать метод на базовом контроллере, например:
public virtual Task BeforeSaveAsync(TProduct product, TProductModel model) =>
Task.CompletedTask;
, а затем вызвать его в своем действии Create
post прямо перед вызовом SaveChangesAsync
.Затем в своих производных контроллерах вы можете переопределить этот метод для выполнения любой дополнительной логики, которая вам может потребоваться.По сути, это позволяет вам заглушить функциональность без необходимости повторения всего действия.Вы можете реализовать столько этих типов крючков, сколько вам нужно или нужно.Например, вы можете захотеть разрешить что-то сделать перед проверкой, после сохранения, до того, как представление будет возвращено и т. Д. Или вы можете захотеть иметь такие вещи, как BeforeCreateAsync
и BeforeUpdateAsync
для реализации различных функций в каждом действии.Это полностью зависит от вас.