我刚刚开始使用ViewModels。你能查看一下这段代码,看看我是否遵循最佳做法?有什么不同寻常的吗?你会以不同的方式进行验证吗?
很抱歉,如果代码很长(有很多部分)。我试图让它尽可能容易理解。
谢谢!
模型
public class CustomerModel
{
[Required(ErrorMessage="Primer nombre!")]
public string FirstName { get; set; }
[Required(ErrorMessage="Segundo nombre!")]
public string LastName { get; set; }
[Required(ErrorMessage="Edad")]
public int? Age { get; set; }
public string State { get; set; }
public string CountryID { get; set; }
[Required(ErrorMessage="Phone Number")]
public string PhoneNumber { get; set; }
}
视图模型
public class CustomerViewModel
{
public CustomerModel Customer { get; set; }
public string Phone1a { get; set; }
public string Phone1b { get; set; }
public string Phone1c { get; set; }
}
控制器
public ActionResult Index()
{
CustomerViewModel Customer = new CustomerViewModel()
{
Customer = new CustomerModel(),
};
return View(Customer);
}
[HttpPost]
public ActionResult Index(CustomerViewModel c)
{
//ModelState.Add("Customer.PhoneNumber", ModelState["Phone1a"]);
// Let's manually bind the phone number fields to the PhoneNumber properties in
// Customer object.
c.Customer.PhoneNumber = c.Phone1a + c.Phone1b + c.Phone1c;
// Let's check that it's not empty and that it's a valid phone number (logic not listed here)
if (!String.IsNullOrEmpty(c.Customer.PhoneNumber))
{
// Let's remove the fact that there was an error!
ModelState["Customer.PhoneNumber"].Errors.Clear();
} // Else keep the error there.
if (ModelState.IsValid)
{
Response.Write("<H1 style'background-color:white;color:black'>VALIDATED</H1>");
}
return View("Index", c);
}
}
查看
@model MVVM1.Models.CustomerViewModel
@using (Html.BeginForm("Index", "Detail"))
{
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td>@Html.LabelFor(m => m.Customer.FirstName)</td>
<td>
@Html.TextBoxFor(m => m.Customer.FirstName)
@Html.ValidationMessageFor(m => m.Customer.FirstName)
</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Customer.LastName)</td>
<td>
@Html.TextBoxFor(m => m.Customer.LastName)
@Html.ValidationMessageFor(m => m.Customer.LastName)
</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Customer.Age)</td>
<td>
@Html.TextBoxFor(m => m.Customer.Age)
@Html.ValidationMessageFor(m => m.Customer.Age)
</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Customer.PhoneNumber)</td>
<td width="350">
@Html.TextBoxFor(m => m.Phone1a, new { size="4", maxlength="3" })
@Html.TextBoxFor(m => m.Phone1b)
@Html.TextBoxFor(m => m.Phone1c)
<div>
@Html.ValidationMessageFor(m => m.Customer.PhoneNumber)
</div>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Submit" /></td>
</tr>
</table>
}
答案 0 :(得分:2)
有一件事突然出现在我身上:
if (ModelState.IsValid)
{
Response.Write("<H1 style'background-color:white;color:black'>VALIDATED</H1>");
}
return View("Index", c);
请记住,视图模型适合将数据传递到控制器并返回到模型。我建议您向视图模型添加一个IsValid属性,然后将其设置为true而不是调用Response.Write。然后只需将其添加到局部视图的顶部:
@if (Model.IsValid)
{
<H1 style'background-color:white;color:black'>VALIDATED</H1>
}
您也可以在视图中访问ModelState,但有些人认为这不是最佳做法。但是,如果您不想在模型中添加属性,只能在视图中看到,您可以这样做:
@if (ViewData.ModelState.IsValid)
另一个挑剔的事情是,MVC验证属性通常用于在UI上进行验证。该验证可以在其他领域重复使用,但在某些情况下是次优的。此外,您可能无法始终修改域模型。因此,为了将我的所有UI验证保存在一个地方,我通常将我的域模型包装在我的视图模型中,这样你就得到这样的结果:
public class CustomerViewModel
{
public CustomerModel Customer { get; set; }
[Required(ErrorMessage="Primer nombre!")]
public string FirstName
{
get { return Customer.FirstName; }
set { Customer.FirstName = value; }
}
...
这似乎是多余的,并且总是值得付出努力,但在使用Entity Framework域模型或其他难以或无法修改的类时,这是一个很好的做法。
答案 1 :(得分:2)
我自己刚刚掌握了MVC,但昨天我研究了同样的话题并得出结论,不应该在ViewModel中直接包含模型对象。所以我的理解是,将CustomerModel直接包含在CustomerViewModel中是不好的做法。
相反,您希望列出要包含在ViewModel中的CustomerModel中的每个属性。然后,您要么手动将CustomerModel中的数据映射到CustomerViewModel,要么使用AutoMapper之类的工具,它会在您的操作方法中使用一行代码自动执行:
public ViewResult Example()
{
// Populate/retrieve yourCustomer here
Customer yourCustomer = new CustomerModel();
var model = Mapper.Map<CustomerModel, CustomerViewModel>(yourCustomer);
return View(model);
}
在这种情况下,Mapper.Map将返回一个可以传递给View的CustomerViewModel。
您还需要在Application_Start方法中包含以下内容:
Mapper.CreateMap<CustomerModel, CustomerViewModel>();
总的来说,我发现AutoMapper很容易上班。当字段名称匹配时它是自动的,如果它们没有,或者您有嵌套的对象,则可以在CreateMap行中指定这些映射。因此,如果您的CustomerModel使用Address对象而不是单个属性,则可以执行以下操作:
Mapper.CreateMap<CustomerModel, CustomerViewModel>()
.ForMember(dest => dest.StreetAddress, opt => opt.MapFrom(src => src.Address.Street));
如果我错了,请任何人纠正我,因为我也只是围绕着MVC。
答案 2 :(得分:1)
我想说你的ViewModel实现非常标准。您正在使用ViewModel充当View和域模型之间的中间对象。这是一种很好的做法。
我唯一感到厌倦的是你如何处理模型错误,而且你的ViewModel应该有一些属性。例如,您可能想要使用RegularExpressionAttribute
类:
public class CustomerViewModel
{
public CustomerModel Customer { get; set; }
[RegularExpression(@"^\d{3}$")]
public string Phone1a { get; set; }
[RegularExpression(@"^\d{3}$")]
public string Phone1b { get; set; }
[RegularExpression(@"^\d{4}$")]
public string Phone1c { get; set; }
}