在MVC中,什么是ViewModel?
在MVC中,什么是ViewModel?
我是ASP.NET MVC的新手。我不太明白ViewModel的目的。
什么是ViewModel,为什么我们需要在ASP.NET MVC应用程序中使用ViewModel?
如果有一个很好的例子来说明它的工作和解释,那就更好了。
视图模型是表示特定视图中使用的数据模型的类。我们可以将这个类作为登录页面的模型:
public class LoginPageVM { [Required(ErrorMessage = "Are you really trying to login without entering username?")] [DisplayName("Username/e-mail")] public string UserName { get; set; } [Required(ErrorMessage = "Please enter password:)")] [DisplayName("Password")] public string Password { get; set; } [DisplayName("Stay logged in when browser is closed")] public bool RememberMe { get; set; } }
使用这个视图模型,您可以定义视图(Razor视图引擎):
@model CamelTrap.Models.ViewModels.LoginPageVM @using (Html.BeginForm()) { @Html.EditorFor(m => m); }
还有一些操作:
[HttpGet] public ActionResult LoginPage() { return View(); } [HttpPost] public ActionResult LoginPage(LoginPageVM model) { ...code to login user to application... return View(model); }
这会产生这个结果(在提交表单后,屏幕上显示验证消息):
正如您所看到的,视图模型拥有许多作用:
- 视图模型通过只包含在视图中显示的字段,来描述视图。
- 视图模型可以使用数据注释或IDataErrorInfo中的特定验证规则。
- 视图模型定义了视图的外观(对于
LabelFor
,EditorFor
和DisplayFor
助手)。 - 视图模型可以结合来自不同数据库实体的值。
- 您可以为视图模型指定易于显示的模板,并使用DisplayFor或EditorFor助手在许多地方重复使用它们。
另一个视图模型的示例及其检索:我们想要显示基本用户数据、他的特权和用户名。我们创建一个特殊的视图模型,它只包含所需的字段。我们从数据库中不同的实体中检索数据,但视图只知道视图模型类:
public class UserVM { public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public bool IsAdministrator { get; set; } public string MothersName { get; set; } }
检索:
var user = db.userRepository.GetUser(id); var model = new UserVM() { ID = user.ID, FirstName = user.FirstName, LastName = user.LastName, IsAdministrator = user.Proviledges.IsAdministrator, MothersName = user.Mother.FirstName + " " + user.Mother.LastName }
视图模型
代表了您希望在视图/页面上显示的数据,无论是用于静态文本还是用于可以添加到数据库(或编辑)的输入值(如文本框和下拉列表)。它与您的领域模型
不同,它是视图的模型。
假设您有一个员工
类,表示您的员工领域模型,它包含以下属性(唯一标识符、名字、姓氏和创建日期):
public class Employee : IEntity { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime DateCreated { get; set; } }
视图模型与领域模型不同之处在于,视图模型仅包含您想要在视图上使用的数据(由属性表示)。例如,假设您想要添加一个新的员工记录,您的视图模型可能看起来像这样:
public class CreateEmployeeViewModel { public string FirstName { get; set; } public string LastName { get; set; } }
正如您所看到的,它仅包含两个属性。这两个属性也在员工领域模型中。为什么会这样呢?Id
可能不是从视图设置的,它可能是由员工表自动生成的。DateCreated
也可以在存储过程或应用程序的服务层中设置。因此,在视图模型中不需要Id
和DateCreated
。在查看已捕获的员工的详细信息(静态文本)时,您可能希望显示这两个属性。
在加载视图/页面时,在员工控制器的创建操作方法中将创建此视图模型的实例,如果需要,则填充任何字段,然后将此视图模型传递给视图/页面:
public class EmployeeController : Controller { private readonly IEmployeeService employeeService; public EmployeeController(IEmployeeService employeeService) { this.employeeService = employeeService; } public ActionResult Create() { CreateEmployeeViewModel model = new CreateEmployeeViewModel(); return View(model); } public ActionResult Create(CreateEmployeeViewModel model) { // Do what ever needs to be done before adding the employee to the database } }
你的视图/页面可能看起来像这样(假设你正在使用 ASP.NET MVC
和 Razor
视图引擎):
@model MyProject.Web.ViewModels.CreateEmployeeViewModel
First Name: | @Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" }) @Html.ValidationMessageFor(m => m.FirstName) |
Last Name: | @Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" }) @Html.ValidationMessageFor(m => m.LastName) |
验证只会在 FirstName
和 LastName
上执行。使用 FluentValidation,你可能会得到这样的验证:
public class CreateEmployeeViewModelValidator : AbstractValidator{ public CreateEmployeeViewModelValidator() { RuleFor(m => m.FirstName) .NotEmpty() .WithMessage("First name required") .Length(1, 50) .WithMessage("First name must not be greater than 50 characters"); RuleFor(m => m.LastName) .NotEmpty() .WithMessage("Last name required") .Length(1, 50) .WithMessage("Last name must not be greater than 50 characters"); } }
而使用数据注解可能是这样的:
public class CreateEmployeeViewModel : ViewModelBase { [Display(Name = "First Name")] [Required(ErrorMessage = "First name required")] public string FirstName { get; set; } [Display(Name = "Last Name")] [Required(ErrorMessage = "Last name required")] public string LastName { get; set; } }
关键要记住的是,视图模型只代表你要使用的数据,其他的不用。如果你有一个拥有30个属性的领域模型,而你只想更新一个属性,你可以想象出所有不必要的代码和验证。在这种情况下,你只需要在视图模型中包含这一个值/属性,而不是领域对象中的全部属性。
视图模型可能不仅包含来自一个数据库表的数据,还可以从另一张表中组合数据。以我上面关于添加新员工记录的例子为例。除了只添加名字和姓氏外,你可能还想添加员工所属的部门。这些部门列表将来自你的 Departments
表。因此,现在你的视图模型中有来自 Employees
和 Departments
表的数据。你只需要添加以下两个属性到你的视图模型中,并填充数据即可:
public int DepartmentId { get; set; } public IEnumerableDepartments { get; set; }
当编辑已经添加到数据库中的员工数据时,它与我上面的例子并没有太大的区别。创建一个视图模型,比如叫做 EditEmployeeViewModel
。只在这个视图模型中包含你想要编辑的数据,比如名字和姓氏。编辑数据并点击提交按钮。不用太担心 Id
字段,因为 Id
值可能已经在 URL 中了,例如:
http://www.yourwebsite.com/Employee/Edit/3
将这个Id
与您的名字和姓氏值一起传递到您的存储库层。
删除记录时,我通常遵循与编辑视图模型相同的路径。我还将具有URL,例如:
http://www.yourwebsite.com/Employee/Delete/3
当视图第一次加载时,我将使用3的Id
从数据库获取员工数据。然后,我会在我的视图/页面中只显示静态文本,以便用户可以看到正在删除哪个员工。当用户单击删除按钮时,我将只使用3的Id
值并将其传递给我的存储库层。您只需要Id
即可从表中删除记录。
另一个要点,您不需要为每个操作创建视图模型。如果它是简单的数据,则只使用EmployeeViewModel
就可以了。如果它是复杂的视图/页面,并且它们彼此不同,则建议为每个使用单独的视图模型。
我希望这可以澄清您对视图模型和域模型的任何困惑。