重建/丰富嵌套或复杂 ViewModel 的好策略是什么?
重建平面 ViewModel 的常用方法是 shown here
但是使用该方法构建和重建嵌套的 ViewModel 过于复杂。
模型
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/