让我们说我有一个基类如下:
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
还有一些继承该基类的类:
public class SomeProduct: Product {
public string Type { get; set; }
}
public class SomeOtherProduct: Product {
public string Model { get; set; }
}
制作用于创建每个产品的创建表单的正确方法是什么?制作不同的控制器并为每个控制器创建页面似乎是多余的。正确的方法是什么?
答案 0 :(得分:0)
您可以创建通用基本控制器:
[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
约束。
对于基本控制器,我使用了AutoMapper中的IMapper
。您可以使用不同的库,以不同的方式处理映射,或者根据需要甚至手动处理。但是,像AutoMapper这样的映射库提供的抽象使这项工作变得更加容易。
当不可避免地需要考虑差异时,可以使用钩子进行处理。例如,您可以在基本控制器上创建一个方法,例如:
public virtual Task BeforeSaveAsync(TProduct product, TProductModel model) =>
Task.CompletedTask;
然后在调用Create
之前的SaveChangesAsync
发布操作中调用它。然后,在派生的控制器中,您可以覆盖此方法以执行您可能需要的任何其他逻辑。基本上,这使您无需重新进行整个操作就可以使用功能。您可以根据需要或需要实现任意数量的此类钩子。例如,您可能希望允许在验证之前,保存之后,返回视图之前进行某些操作。或者,甚至可能希望使用BeforeCreateAsync
和BeforeUpdateAsync
之类的东西来实现不同的功能在每个动作中。完全取决于您。