c# - 在 !ModelState.IsValid 上重建嵌套的 ViewModel

标签 c# asp.net-mvc asp.net-mvc-4 mvvm

重建/丰富嵌套或复杂 ViewModel 的好策略是什么?

重建平面 ViewModel 的常用方法是 shown here

但是使用该方法构建和重建嵌套的 ViewModel 过于复杂。

enter image description here

模型

public class PersonInfo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Nationality { get; set; }
    public List<Address> Addresses { get; set; }
}

public class Address
{
    public int AddressTypeID { get; set; }
    public string Country { get; set; }
    public string PostalCode { get; set; }
}

public class AddressType
{ 
    public int Id { get; set; }
    public string Description { get; set; }
}

查看模型

public class PersonEditModel
{
    public int Id { get; set; }
    public string Name { get; set; } //read-only
    public int Nationality { get; set; }
    public List<AddressEditModel> Addresses { get; set; }
    public List<SelectListItem> NationalitySelectList { get; set; } //read-only
}

public class AddressEditModel
{
    public int AddressTypeId { get; set; }
    public string AddressDescription { get; set; } //read-only
    public string Country { get; set; }
    public string PostalCode { get; set; }
    public List<SelectListItem> CountrySelectList { get; set; } //read-only
}

Action

public ActionResult Update(int id)
{
   var addressTypes = service.GetAddressTypes();
   var person = service.GetPerson(id);
   var personEditModel= Map<PersonEditModel>.From(person);

   foreach(var addressType in addressTypes)
   {
      var address = person.Addresses.SingleOrDefault(i => i.AddressTypeId == addressType.Id)
      if(address == null)
      {
          personEditModel.Addresses.Add(new AddressEditModel
          {
              AddressTypeId = addressType.Id
          });
      }
      else
      {
          personEditModel.Addresses.Add(Map<AddressEditModel>.From(address));
      }
   }

   EnrichViewModel(personEditModel, person, addressTypes); //populate read-only data such as SelectList
   return Index(personEditModel);
}

[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
   if(!ModelState.IsValid)
   {
       var person = service.GetPerson(editModel.Id);
       var addressTypes = service.GetAddressTypes();
       EnrichViewModel(editModel, person, addressTypes); 
       return View(editModel);
   }

   service.Save(...);
   return RedirectToAction("Index");
}

//populate read-only data such as SelectList
private void EnrichViewModel(PersonEditModel personEditModel, Person person, IEnumerable<AddressType> addressTypes)
{
    personEditModel.Name = person.Name;
    personEditModel.NationalitySelectList = GetNationalitySelectList();

    foreach(var addressEditModel in personEditModel.Addresses)
    {
        addressEditModel.Description = addressTypes.Where(i => i.Id = addressEditModel.AddressTypeId).Select(i => i.Description).FirstOrDefault();
        addressEditModel.CountrySelectListItems = GetCountrySelectList(addressEditModel.AddressTypeId);
    }
}

我用于构建和重建 ViewModel(PersonEditModel 和 AddressEditModel)的代码太丑陋了。我该如何重组我的代码来清理这个烂摊子?

一个简单的方法是始终构建一个新的 View 模型而不是合并/重建,因为 MVC 无论如何都会用 ModelState 中的值覆盖字段

[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
   if(!ModelState.IsValid)
   {
       var newEditModel = BuildPersonEditModel(editModel.Id);
       return View(newEditModel);
   }

但我不确定这是个好主意。是吗?除了 AJAX 还有其他解决方案吗?

最佳答案

我将逐一解决您的具体痛点,并在此过程中尝试展示我自己的经验和可能的解决方案。恐怕这里没有最佳答案。你只需要选择邪恶中较小的一个。

重建下拉列表

  • 他们是婊子!当您重新呈现页面时,无法逃避重建它们。虽然 HTML 表单善于记住选定的索引(它们会 happily restore it for you ),但您必须重建它们。如果您不想重建它们,请切换到 Ajax。

重建 View 模型的其余部分(甚至嵌套)

  • HTML 表单擅长为您重建整个模型,只要您坚持使用输入和隐藏字段以及其他表单元素(选择、文本区域等)。

  • 如果您不想重建数据,就无法避免回传数据,但在这种情况下,您需要问问自己 - 哪个更有效 - 回传少数额外的字节或进行另一个查询以获取丢失的部分?

  • 如果您不想回发只读字段,但仍希望模型绑定(bind)器正常工作,您可以通过 [Bind(Exclude="Name,SomeOtherProperty")] 在 View 模型类上。在这种情况下,您可能需要在将它们发送回浏览器之前再次设置它们。

    // excluding specific props. note that you can also "Include" instead of "Exclude".
    [Bind(Exclude="Name,NationalitySelectList")]
    public class PersonEditModel
    {
        ...
    
  • 如果您排除这些属性,则不必求助于隐藏字段并将它们发回 - 因为模型绑定(bind)器会简单地忽略它们,您仍然会获得需要填充的值。

  • 就我个人而言,我使用仅包含可发布数据的编辑模型而不是 Bind 魔法。除了避免使用 Bind 所需的魔术字符串外,它们还为我提供了强类型化和更清晰意图的好处。我使用自己的映射器类来进行映射,但您也可以使用 Automapper 之类的东西来为您管理映射。

  • 另一个想法可能是在 Session 中缓存初始 ViewModel,直到成功执行 POST。这样,您就不必从头开始重建它。您只需将初始的与提交的合并,以防出现验证错误。

我每次使用 Forms 时都会进行同样的战斗,最后,我开始接受它并完全使用 AJAX 来处理任何不是简单的名称-值集合类型表单的东西。除了不会让人头疼之外,它还可以带来更好的用户体验。

附言您发布的链接本质上与您正在做的事情相同 - 只是它使用映射器框架在域和 View 模型之间映射属性。

关于c# - 在 !ModelState.IsValid 上重建嵌套的 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25589933/

相关文章:

c# - MVC4 加载缓慢我该怎么办?

c# - 在 LINQ 中按列表分组

c# - 我的 url 编码有什么问题?

c# - 如何在 Visual Studio 2008 中调试 *Invoke()

.net - 包管理器控制台不工作

asp.net-mvc - 发送批量通知电子邮件而不阻止

c# - EF 5 启用迁移 : No context type was found in the assembly

c# - 上传 HttpPostedFileBase 文件和一些参数

c# - Xaml在半页上显示 map ,另一半以xamarin形式显示信息

c# - 通过单击 ListView (Xamarin.Forms) 中的按钮获取当前项