【问题标题】:Best practice to design a ViewModel containing display data and binding data for HttpGet and HttpPost为 HttpGet 和 HttpPost 设计包含显示数据和绑定数据的 ViewModel 的最佳实践
【发布时间】:2020-02-18 14:58:28
【问题描述】:

我已经阅读 StackOverflow 很长一段时间了,是时候发表我的第一篇文章了:

我目前正在使用 C# / ASP.NET Core 3.1。为 HttpGet 和 HttpPost 设计包含显示数据和绑定数据的 ViewModel 的最佳实践是什么。我发现了一些关于保持显示和绑定数据分开或不分开但没有任何示例的讨论。在 HttpPost 上使用相同的 ViewModel(没有绑定属性)的问题是 Display Data 引用会获得无效的模型状态,因为它们在传入模型中当然是 null。

我通常倾向于使用以下精简代码。继承是要走的路还是任何其他类型的模式?还是将所有内容都放在具有非绑定数据的绑定属性的视图模型中更好?

谢谢大家!

public class CMyBindModel
{
  public int ProductQuantity ( get; set; }    // Binding Data

  // Other Binding properties ...
}

public class CMyViewModel : CMyBindModel
{
  public string ProductName ( get; set; }    // Display Data

  // Other Display properties ...
}

public IActionResult Cart ()
{
  CMyViewModel viewModel = BuildViewModel () ;
  return View (viewModel) ;
}

[HttpPost]
public IActionResult Cart (CMyBindModel bindModel)
{
  if (ModelState.IsValid)
  {
    SetProductQuantity (bindModel.ProductQuantity) ;
    return RedirectToAction ("Cart") ;
  }

  CMyViewModel viewModel = BuildViewModel () ;
  return View (viewModel) ;
}

Cart.cshtml

@model CMyViewModel

<div>@Model.ProductName</div>

<form method="post" asp-area="" asp-controller="CartController" asp-action="Cart">
  <input asp-for="ProductQuantity" />
  <button type="submit">Update</button>
</form>

【问题讨论】:

  • 您的第一个模型描述了请求的样子。您的视图模型描述了渲染视图所需的内容。两者可能看起来相似,但他们显然对不同的事情负责。也就是说:我会使用两个类。没有继承。如果您在视图中需要请求中的部分,请复制属性。 - 只是我的 5 美分。
  • 这里有一些策略。我经常使用您概述的继承方法,尤其是当每个视图模型都特定于底层绑定模型时。例如,如果User 实体有一个应该显示的只读DateJoined 属性,那么这是UserViewModel 的一个很好的用例。对于视图模型具有跨绑定模型使用的通用属性集的区域,我改为使用具有通用绑定模型属性的通用视图模型。这包含常见的只读元数据和共享用户界面元素,例如PageTitleLastModified
  • 非常感谢大家的建议。是的,我猜不同的策略在不同的情况下是有用的(继承,分离模型,ModelState.Remove,...)。纯粹主义者可能不同意继承解决方案,因为大多数时候,它不是 isA 关系。我很少使用 2 个单独的模型,但我最近有一个模型,其中包含商店类列表,其中包含要显示的产品类列表,每个类中都有一些绑定属性 - 用于 View 和 Binding 的 2 个单独模型解决了我的问题。干杯。

标签: c# asp.net-core


【解决方案1】:

在 HttpPost 上使用相同的 ViewModel(没有绑定属性)的问题是 Display Data 引用会获得无效的模型状态,因为它们在传入模型中当然是 null。

通过使用相同的 ViewModel(这里是 CMyViewModel),您可以通过在判断 ModelState.IsValid 之前从 ModelState 中删除字段来克服验证错误。

参考我的简单演示:

型号:

public class CMyViewModel 
{
    [Required]
    public int ProductQuantity { get; set; }  // Binding Data

    // Other Binding properties ...

    [Required]
    public string ProductName{get; set; }    // Display Data

    // Other Displaying properties ...
}

动作:

public void RemoveDisplayingDataValidation()
{
    ModelState.Remove("ProductName");
    //remove other Displaying properties' validation
}

[HttpPost]
public IActionResult Cart(CMyViewModel viewModel)
{
    RemoveDisplayingDataValidation();
    if (ModelState.IsValid)
    {
        SetProductQuantity (viewModel.ProductQuantity) ;
        return RedirectToAction("Cart");
    }

    CMyViewModel viewModel = BuildViewModel () ;
    return View(viewModel);
}

参考ModelState.IsValid does not exclude required property

此外,Christoph Lütjen 提出的分别使用两个模型的建议也将奏效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 2016-09-17
    • 2010-09-08
    • 2018-11-03
    相关资源
    最近更新 更多