重建/丰富嵌套或复杂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
}
操作
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)的代码太难看了。如何重构我的代码来清理这个烂摊子?
一种简单的方法是始终构建新的视图模型而不是合并/重建,因为MVC将使用ModelState中的值覆盖字段
[HttpPost]
public ActionResult Update(PersonEditModel editModel)
{
if(!ModelState.IsValid)
{
var newEditModel = BuildPersonEditModel(editModel.Id);
return View(newEditModel);
}
但我不确定这是个好主意。是吗?除AJAX之外还有其他解决方案吗?
答案 0 :(得分:3)
我将逐一解决您的具体痛点,并尝试在此过程中展示自己的经验和可能的解决方案。我担心这里没有最好的答案。你只需选择较少的邪恶。
重建下拉列表
重建其余视图模型(甚至嵌套)
HTML表单擅长为您重建整个模型,只要您坚持输入和隐藏字段以及其他表单元素(选择,文本区域等)。
如果您不想重建数据,则无法避免回发数据,但在这种情况下,您需要问问自己 - 哪一个更有效 - 发布返回几个额外的字节或进行另一个查询以获取缺失的部分?
如果您不想回发只读字段,但仍希望模型绑定器正常工作,则可以通过视图模型类上的[Bind(Exclude="Name,SomeOtherProperty")]
排除属性。在这种情况下,您可能需要再次设置它们,然后再将它们发送回浏览器。
// excluding specific props. note that you can also "Include" instead of "Exclude".
[Bind(Exclude="Name,NationalitySelectList")]
public class PersonEditModel
{
...
如果您排除这些属性,则不必使用隐藏字段并将其发回 - 因为模型绑定器只会忽略它们,您仍然会获得需要填充的值。 / p>
就个人而言,我使用的编辑模型只包含可发布的数据,而不是Bind
魔法。除了像Bind一样避免使用魔法弦之外,它们还给我带来强大打字和更清晰意图的好处。我使用自己的映射器类来进行映射,但您也可以使用Automapper之类的东西来管理映射。
另一个想法可能是在Session中缓存初始ViewModel,直到成功发布POST。这样,您就不必从头开始重建它。在验证错误的情况下,您只需将初始的一个与提交的一个合并。
每次我使用Forms时,我都会进行同样的战斗,最后,我已经开始吮吸它并完全使用AJAX来处理任何不是简单的名称 - 值集合类型的形式。除了头痛之外,它还可以带来更好的用户体验。
P.S。您发布的链接基本上与您正在执行的操作相同 - 只是它使用映射器框架来映射域和视图模型之间的属性。