为共享基类的产品创建“创建”页面的正确方法是什么

时间:2019-04-04 18:36:25

标签: c# asp.net asp.net-mvc asp.net-core

让我们说我有一个基类如下:

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; }

    }

制作用于创建每个产品的创建表单的正确方法是什么?制作不同的控制器并为每个控制器创建页面似乎是多余的。正确的方法是什么?

1 个答案:

答案 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发布操作中调用它。然后,在派生的控制器中,您可以覆盖此方法以执行您可能需要的任何其他逻辑。基本上,这使您无需重新进行整个操作就可以使用功能。您可以根据需要或需要实现任意数量的此类钩子。例如,您可能希望允许在验证之前,保存之后,返回视图之前进行某些操作。或者,甚至可能希望使用BeforeCreateAsyncBeforeUpdateAsync之类的东西来实现不同的功能在每个动作中。完全取决于您。