什么是MVC中的ViewModel?

479

我是ASP.NET MVC的新手。我不太理解ViewModel的目的。

什么是ViewModel?为什么在ASP.NET MVC应用程序中需要使用ViewModel?

如果能提供一个好的工作示例和解释会更好。


4
这篇文章是关于“什么是ASP.NET MVC视图模型(ViewModel)?”的内容,您正在寻找这个。 - Yusubov
6
此文章看起来很不错:http://www.rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp.net-mvc-applications - Andrew
可能是在MVC中,什么是ViewModel?的重复问题。 - rogerdeuce
16个回答

685
一个“视图模型”代表你想要在视图/页面上展示的数据,可以用于静态文本或输入值(比如文本框和下拉列表),这些值可以添加到数据库中(或编辑)。它与你的“领域模型”不同。它是视图的一个模型。
假设你有一个名为“Employee”的类,代表你的员工领域模型,它包含以下属性:唯一标识符、名字、姓氏和创建日期。
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 可能也会在存储过程或应用程序的服务层中设置。因此,视图模型中不需要 IdDateCreated。当查看已捕获的员工的详细信息(静态文本)时,您可能希望显示这两个属性。

加载视图/页面时,员工控制器中的创建操作方法将创建此视图模型的实例,如果需要,则填充任何字段,然后将此视图模型传递给视图/页面:

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 MVCRazor 视图引擎,则您的视图/页面可能如下所示:

@model MyProject.Web.ViewModels.CreateEmployeeViewModel

<table>
     <tr>
          <td><b>First Name:</b></td>
          <td>@Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.FirstName)
          </td>
     </tr>
     <tr>
          <td><b>Last Name:</b></td>
          <td>@Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.LastName)
          </td>
     </tr>
</table>

因此,只需对FirstNameLastName进行验证。使用FluentValidation,您可以像这样进行验证:

public class CreateEmployeeViewModelValidator : AbstractValidator<CreateEmployeeViewModel>
{
     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表。因此,现在您可以在一个视图模型中拥有来自EmployeesDepartments表的数据。然后只需向视图模型添加以下两个属性并用数据填充:

public int DepartmentId { get; set; }

public IEnumerable<Department> Departments { get; set; }

当编辑已添加到数据库中的员工数据时,与上述示例相比并没有太大区别。创建一个视图模型,例如称为EditEmployeeViewModel。只在此视图模型中包含要编辑的数据,如名字和姓氏。编辑数据并单击提交按钮。我不会过于担心Id字段,因为Id值很可能已经在URL中了,例如:

http://www.yourwebsite.com/Employee/Edit/3

将此Id与您的名字和姓氏值一起传递到存储库层。

删除记录时,通常会按照编辑视图模型相同的路径进行操作。例如,我也会有一个 URL:

http://www.yourwebsite.com/Employee/Delete/3

当视图首次加载时,我将使用Id为3的员工数据从数据库中获取。然后,在我的视图/页面上只显示静态文本,以便用户可以看到要删除的员工是谁。当用户单击“删除”按钮时,我将仅使用3的Id值并将其传递给我的存储库层。您只需要Id即可从表中删除记录。

另一个要点是,并非每个操作都需要一个视图模型。如果只是简单的数据,则只使用EmployeeViewModel就足够了。如果是复杂的视图/页面并且它们彼此不同,则建议为每个使用单独的视图模型。

希望这能澄清您对视图模型和领域模型的任何困惑。


6
@Kenny: 那就展示一下吧 :) 我想说的是,假设你有一个包含50个属性的领域模型,而你的视图只需要显示5个属性,那么发送所有50个属性来显示5个属性是没有意义的。请精简发送内容。 - Brendan Vogt
7
@BrendanVogt,你解释的很好,但我不明白“发送所有50个属性”的成本是什么。其他代码已经创建了一个包含全部50个属性的模型对象,如果只是为了不发送45个属性而维护另一个类似的对象似乎并不值得,特别是如果将来可能需要发送这45个属性中的任何一个。请帮我翻译。 - Kenny Evitt
5
@BrendanVogt - 我觉得LukLed的答案或许能帮助我理解为什么ViewModel可能有用,特别是它可以“(可以)将来自不同数据库实体的值组合在一起”[我假设如果“数据库实体”被替换为“对象模型”,这个短语仍然是正确的]。但是,ViewModel的具体目的是解决什么问题?你有任何链接吗?我自己找不到任何东西。[如果我似乎在针对你,请接受我的道歉!] - Kenny Evitt
2
我刚听到有人说ViewModels是一种很好的方法,可以将多个集合(或跨模型属性)发送到单个视图中,而无需将它们塞入viewBag中。这对我来说很有道理。 - Ayyash
4
很抱歉需要提出批评,但是这个答案不完整。将视图模型定义为仅在页面上显示所需的内容,就像问“什么是汽车?”并得到答案“它不是飞机一样”。嗯,这是正确的,但并没有太大帮助。更准确的 VM 定义应该是“渲染页面所需的所有内容”。如果你看到底部,我已经列出了构建 VM 的组件,可以在许多情况下利用现有的领域模型和展示模型来轻松地构建你的 VM。 - user3230660
显示剩余10条评论

143

视图模型是一个表示特定视图中使用的数据模型的类。我们可以将此类用作登录页面的模型:

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);
    <input type="submit" value="Save" class="submit" />
}

还有一些操作:

[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
} 

我认为 user.Mother.FirstName + " " + user.Mother.LastName 应该在视图模型端完成。所有逻辑都应该在视图模型端完成。 - Kurkula
4
@Chandana:我认为可以在视图模型中进行简单的字符串拼接。如果它们本来就是要一起呈现的,那就没必要暴露两个字段。 - LukLed

97

编辑:我在我的博客上更新了这篇答案:

http://www.samwheat.com/post/The-function-of-ViewModels-in-MVC-web-development

我的答案有点长,但我认为比较View Model和其他常用模型类型很重要,以便理解它们的不同之处以及它们的必要性。

总而言之,直接回答问题:

一般来说,视图模型是一个包含呈现视图所需的所有属性和方法的对象。视图模型属性通常与数据对象(如客户和订单)相关,并且还包含与页面或应用程序本身相关的属性,例如用户名、应用程序名称等。视图模型提供了一个方便的对象,可传递给渲染引擎以创建HTML页面。使用视图模型的众多原因之一是,视图模型提供了一种单元测试特定呈现任务的方法,例如处理用户输入、验证数据、检索用于显示的数据等。

以下是实体模型(又名DTO、模型)、演示模型和视图模型的比较。

数据传输对象(DTO)又名“模型”

数据传输对象(DTO)是一个具有与数据库中的表模式相匹配的属性的类。DTO是根据其常见用途来回传输数据的名称。
DTO的特点:

  • 是业务对象 - 它们的定义取决于应用程序数据。
  • 通常仅包含属性 - 没有代码。
  • 主要用于从数据库传输数据。
  • 属性与数据存储中特定表上的字段完全或接近匹配。

数据库表通常是规范化的,因此DTO通常也是规范化的。这使它们在呈现数据方面的用途有限。但是,对于某些简单的数据结构,它们通常做得很好。

以下是DTO可能看起来像的两个示例:

public class Customer
{
    public int ID { get; set; }
    public string CustomerName { get; set; }
}


public class Order
{
    public int ID { get; set; }
    public int CustomerID { get; set; }
    public DateTime OrderDate { get; set; }
    public Decimal OrderAmount { get; set; }
}

演示模型

演示模型是一个实用类,用于在屏幕或报告上呈现数据。演示模型通常用于建模由多个DTO组成的复杂数据结构。演示模型通常表示数据的去规范化视图。

演示模型的特点:

  • 是业务对象-它们的定义依赖于应用程序数据。
  • 主要包含属性。代码通常仅限于格式化数据或将其转换为DTO或从DTO中转换数据。演示模型不应包含业务逻辑。
  • 经常呈现数据的非规范化视图。也就是说,它们经常合并来自多个DTO的属性。
  • 经常包含与DTO不同基础类型的属性。例如,美元金额可以表示为字符串,以便包含逗号和货币符号。
  • 通常通过它们的使用方式以及它们的对象特性进行定义。换句话说,在渲染网格的支持模型的上下文中,一个简单的DTO实际上也是演示模型。

演示模型根据需要和位置使用(而DTO通常与数据库模式相关联)。演示模型可以用于对整个页面、页面上的网格或下拉菜单上的网格进行建模。演示模型通常包含其他演示模型的属性。演示模型通常用于单次使用,例如在单个页面上呈现特定网格。

一个演示模型的例子:

public class PresentationOrder
{
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

视图模型

视图模型类似于展示模型,用于渲染视图的后备类。 但是,与演示模型或DTO的构造方式非常不同。 视图模型通常包含与演示模型和DTO相同的属性,因此它们经常被混淆。

视图模型的特点:

  • 是用于呈现页面或屏幕的单个数据源。 通常,这意味着视图模型将公开页面上的任何控件需要正确呈现自身所需的每个属性。 将视图模型作为视图的单个数据源极大地提高了其进行单元测试的能力和价值。
  • 是包含应用程序数据及应用程序代码的组合对象。 当设计具有可重用性的视图模型时,此特性至关重要,并在以下示例中进行了讨论。
  • 包含应用程序代码。 视图模型通常包含在呈现期间和用户与页面交互时调用的方法。 此代码通常涉及事件处理,动画,控件的可见性,样式等内容。
  • 包含调用业务服务以检索数据或将其发送到数据库服务器的代码。 这种代码通常被错误地放置在控制器中。 从控制器调用业务服务通常会限制视图模型进行单元测试的有用性。 明确地说,视图模型本身不应包含业务逻辑,但应调用包含业务逻辑的服务。
  • 通常包含其他页面或屏幕的其他视图模型属性。
  • 是“每页”或“每个屏幕”编写的。 通常为应用程序中的每个页面或屏幕编写唯一的视图模型。
  • 通常派生自基类,因为大多数页面和屏幕共享公共属性。

视图模型组合

如前所述,视图模型是复合对象,因为它们将应用程序属性和业务数据属性组合在单个对象上。 常用于视图模型的应用程序属性示例包括:

  • 用于显示应用程序状态(例如错误消息,用户名,状态等)的属性。
  • 用于格式化,显示,样式化或动画化控件的属性。
  • 用于数据绑定的属性,例如列表对象和保存由用户输入的中间数据的属性。

以下示例说明了视图模型的组合性质的重要性以及如何构建高效且可重用的视图模型。

假设我们正在编写一个Web应用程序。 应用程序设计的一项要求是在每个页面上显示页面标题,用户名和应用程序名称。 如果我们想创建一个页面来显示演示订单对象,则可以将演示模型修改如下:

public class PresentationOrder
{
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
    public string CustomerName { get; set; }
    public Decimal OrderAmount { get; set; }
    public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}

这个设计可能可行...但是如果我们想创建一个显示订单列表的页面呢?PageTitle、UserName和ApplicationName属性将被重复使用,变得难以处理。此外,如果我们想在类的构造函数中定义一些页面级别的逻辑,又该怎么办?如果我们为每个要显示的订单创建一个实例,就不能再这样做了。
组合优于继承
以下是我们可以重新设计订单展示模型的方式,使其成为真正的视图模型,并且对于显示单个PresentationOrder对象或PresentationOrder对象集合非常有用:
public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public PresentationOrder Order { get; set; }
}


public class PresentationOrderVM
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

从上面的两个类可以看出,我们可以将视图模型视为包含另一个演示模型作为属性的演示模型之一。顶层演示模型(即视图模型)包含与页面或应用程序相关的属性,而演示模型(属性)包含与应用程序数据相关的属性。

我们可以进一步设计并创建一个基础视图模型类,它不仅可以用于PresentationOrders,还可以用于任何其他类:

public class BaseViewModel
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
}

现在我们可以简化PresentationOrderVM,如下所示:
public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public PresentationOrder Order { get; set; }
}

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

我们可以通过将BaseViewModel变成通用的来使其更具可重用性:
public class BaseViewModel<T>
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }

    // Business property
    public T BusinessObject { get; set; }
}

现在我们的实现变得轻松无比:
public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
    // done!
}

public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
    // done!
}

2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Chef_Code
1
@Sam:“视图模型通常包含与表示模型和DTO相同的属性,因此它们经常被混淆。”这是否意味着它们通常被用作表示模型的替代品,还是它们应该包含表示模型/DTO? - Alexander Derck
2
@AlexanderDerck 它们用于不同的目的。它们会被错误地混淆。通常情况下,您不会使用一个表示模型来代替视图模型。更常见的情况是,VM“包含”表示模型,即 MyViewModel<MyPresModel> - user3230660
2
假设模型对象是实时对象,例如nhibernate模型...那么通过具有BusinessObject,我们不是直接将模型/实时对象暴露给视图吗?也就是说,业务对象可以用来直接修改数据库状态?另外,嵌套视图模型怎么办?那会需要多个业务对象属性,对吧? - Muhammad Ali
好的想法,都很不错。谢谢Sam。 - InteXX
显示剩余2条评论

28

我并没有读完所有的帖子,但每个答案似乎都缺少一个真正帮助我理解的概念...

如果模型类似于数据库的表格,那么ViewModel就相当于数据库的视图 - 视图通常可以从单个表返回少量数据,也可以从多个表(联接)返回复杂的数据集。

我发现自己使用ViewModel将信息传递到视图/表单中,然后在表单提交回控制器时将数据转换为有效的Model - 对于存储Lists(IEnumerable)也非常方便。


22

如果您有视图特定的属性,而不是与数据库/服务/数据存储相关的属性,使用ViewModels是一个很好的做法。例如,您想要基于一个数据库字段(或两个字段)保留复选框的选择状态,但该数据库字段本身不是布尔类型。虽然可以在模型本身中创建这些属性,并将其隐藏不与数据绑定,但根据此类字段和事务的数量,您可能不想使模型变得混乱。

如果视图特定的数据和/或转换太少,您可以直接使用模型。


14

有很多大的例子,让我用清晰简洁的方式解释一下。

ViewModel = 为视图创建的模型。

ASP.NET MVC 视图无法拥有多个模型,因此如果我们需要在视图中显示来自多个模型的属性,则不可能。ViewModel 就是为了服务于这个目的。

View Model 是一个只能容纳那些视图所需属性的模型类。它还可以包含来自数据库中多个实体(表)的属性。顾名思义,这个模型是专门为 View 的要求而创建的。

以下是一些 View Models 的例子:

  • 要在视图页面中列出来自多个实体的数据 - 我们可以创建一个 ViewModel,并具有我们想要列出数据的所有实体的属性。将这些数据库实体连接起来并设置 ViewModel 属性,然后返回到视图以显示不同实体的数据
  • 视图模型可以仅定义单个实体的特定字段,这是视图所需的。

ViewModel 还可以用于向多个实体插入和更新记录,但 ViewModel 的主要用途是将多个实体(模型)的列显示在单个视图中。

创建 ViewModel 的方法与创建 Model 的方法相同,为 ViewModel 创建视图的方法与为 Model 创建视图的方法相同。

这里有一个小例子,演示了如何使用 ViewModel 列出数据

希望这对你有用。


13

视图模型 a 是一个简单的类,可以包含多个类属性。我们使用它来继承所有所需的属性,例如,我有两个类:学生和课程。

Public class Student
{
public int Id {get; set;}
public string Name {get; set;}
}  
Public class Subject
{
public int SubjectID {get; set;}
public string SubjectName {get; set;}
}

现在我们希望在视图中(在MVC中)显示学生姓名和科目姓名的记录,但是添加多个类似以下的类是不可能的:

 @model ProjectName.Model.Student  
 @model ProjectName.Model.Subject

上述代码将抛出一个错误...

现在我们创建了一个类,可以赋予任何名称,但采用“XyzViewModel”这种格式更易于理解。这是继承的概念。 现在我们创建了第三个类,并给它以下名称:

public class StudentViewModel:Subject
{
public int ID {get; set;}
public string Name {get; set;}
}

现在我们在视图中使用这个ViewModel

@model ProjectName.Model.StudentViewModel

现在我们可以访问StudentViewModel及其继承类的所有属性。


是的,我同意所有的观点,但你没有展示如何让虚拟机属性有意义(例如在控制器和/或服务/接口中)。这是能够在ASP.NET MVC中使用虚拟机的重要部分。 - Kris Bunda

11

MVC没有View Model:它有Model、View和Controller。ViewModel是MVVM(Model-View-ViewModel)的一部分。MVVM源自Presentation Model并在WPF中广泛使用。MVVM中也应该有一个Model,但大多数人完全忽略了这种模式的重点,他们只会有一个View和一个ViewModel。MVC中的Model与MVVM中的Model类似。

在MVC中,流程被分为三个不同的职责:

  • View负责向用户呈现数据
  • Controller负责页面流程
  • Model负责业务逻辑

MVC不太适合Web应用程序。这是Smalltalk为创建桌面应用程序引入的一种模式。Web环境的行为完全不同。将40年前的概念从桌面开发中复制并粘贴到Web环境中并不太有意义。然而,很多人认为这样做没问题,因为他们的应用程序可以编译并返回正确的值。在我看来,这还不足以将某种设计选择声明为可行。

Web应用程序中Model的一个例子可能是:

public class LoginModel
{
    private readonly AuthenticationService authentication;

    public LoginModel(AuthenticationService authentication)
    {
        this.authentication = authentication;
    }

    public bool Login()
    {
        return authentication.Login(Username, Password);
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

控制器可以这样使用它:

public class LoginController
{
    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        bool success = model.Login();

        if (success)
        {
            return new RedirectResult("/dashboard");
        }
        else
        {
            TempData["message"] = "Invalid username and/or password";
            return new RedirectResult("/login");
        }
    }
}

你的控制器方法和模型将会小而简洁,易于测试并且专注于要点。


感谢您对MVVM架构的深入洞察,但为什么MVC不行呢?您的推理值得怀疑,可能存在偏袒。尽管我对MVVM一无所知,但如果像MVC这样的架构可以模仿其行为而无需编写50k行代码,那有什么大不了的呢? - Chef_Code
@Chef_Code:这不是可疑或偏袒:只需阅读有关MVC的原始论文。回到源头比盲目地跟随群众(即“最佳实践”)要好得多。 MVC适用于更小的单元:例如,屏幕上的按钮由模型,视图和控制器组成。在Web-MVC中,整个页面都有一个控制器,一个模型和一个视图。模型和视图应该连接起来,以便模型中的更改立即反映在视图中,反之亦然。模仿是一件非常重要的事情。架构不应欺骗开发人员。 - Jeroen
1
MVC这个缩写已经被盗用和篡改了。是的,MVC没有VM,但它也没有Repository或服务层,而这些对象在网站中被广泛使用。我认为OP的问题是“如何在MVC中引入和使用VM”。在MVC的新含义中,模型不是业务逻辑所在的地方。业务逻辑应该在使用MVC或MVVM的Web或桌面应用程序的服务层中。术语模型描述了传递到/从服务层的业务对象。这些定义与MVC的原始描述大相径庭。 - user3230660
1
@Sam 不是网站的所有内容都可以称为MVC的一部分。 MVC没有新的含义。 有正确的含义和“与MVC完全无关但人们混淆的东西”的含义。说模型负责业务逻辑并不意味着业务逻辑在模型中编码。大多数情况下,模型充当应用程序的外观。 - Jeroen
我认为Microsoft的MVC的主要缺陷在于将Model与View锁定。这本身就违背了过去20年N-Tier设计中一直进行的所有分离的目的。他们浪费了我们的时间,强制我们在2002年使用“WebForms”,这是另一种桌面模型,被提升到Web世界。现在他们抛弃了它,但又在这个新的Web开发范式中提出了另一个桌面模型。与此同时,Google和其他公司正在构建巨大的客户端模型来实现所有分离。我认为1998年的旧ASP VBScript才是他们最真实的Web开发系统。 - Stokely

7

ViewModel是MVC框架中修补概念笨拙的一种方法。它代表了3层模型-视图-控制器架构的第4层。当模型(领域模型)不适合或太大(超过2-3个字段)用于视图时,我们创建一个较小的ViewModel来传递给视图。


2

视图模型是一个类,我们可以用它来在视图上呈现数据。假设你有两个实体 Place 和 PlaceCategory,并且你想要使用一个单一的模型从这两个实体中访问数据,那么我们就使用 ViewModel。

public class Place
    {
       public int PlaceId { get; set; }
        public string PlaceName { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string BestTime { get; set; }
    }
    public class Category
    {
        public int ID { get; set; }
        public int? PlaceId { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }
    public class PlaceCategoryviewModel
    {
        public string PlaceName { get; set; }
        public string BestTime { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }

在上面的示例中,Place和Category是两个不同的实体,而PlaceCategory ViewModel是ViewModel,我们可以在View上使用它。"最初的回答"

你的例子不是很清晰。上面所述的是ViewModel将数据连接到其视图。如果您查看BlipAjax中的ViewModels,您会看到完全适合它的类。 - Herman Van Der Blom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接