asp.net mvc - mvc教程 - 什么是MVC中的ViewModel?



asp.net-mvc (6)

我是ASP.NET MVC新手。 我在理解ViewModel的目的方面存在问题。

什么是ViewModel ,为什么我们需要一个ASP.NET MVC应用程序的ViewModel

如果我能有一个简单的例子,那会更好。

https://ffff65535.com


MVC没有视图模型:它有一个模型,视图和控制器。 视图模型是MVVM(Model-View-Viewmodel)的一部分。 MVVM源自演示模型,并在WPF中得到推广。 MVVM中也应该有一个模型,但大多数人完全没有注意到这种模式,他们只会有一个视图和一个视图模型。 MVC中的模型与MVVM中的模型相似。

在MVC中,这个过程分为3个不同的职责:

  • View负责将数据呈现给用户
  • 控制器负责页面流
  • 模型负责业务逻辑

MVC不太适合Web应用程序。 这是Smalltalk为创建桌面应用程序而推出的一种模式。 Web环境的行为完全不同。 从桌面开发中复制40年前的概念并将其粘贴到网络环境中没有多大意义。 然而,很多人认为这是好的,因为他们的应用程序编译并返回正确的值。 也就是说,在我看来,还不足以宣布某种设计选择。

Web应用程序中的模型示例可能是:

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");
        }
    }
}

您的控制器方法和您的模型将很小,易于测试并符合要求。


ViewModel是修补MVC框架概念笨拙的工作周。 它代表了3层模型 - 视图 - 控制器体系结构中的第4层。 当模型(域模型)不适合,视图太大(大于2-3个字段)时,我们创建较小的ViewModel以将其传递给视图。


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

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

我的答案有点冗长,但我认为将视图模型与其他类型的常用模型进行比较以了解它们为什么不同以及为什么它们是必要的,这一点很重要。

总结并直接回答所问的问题:

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

以下是实体模型(a.ka.DTO的a.ka.模型),演示模型和视图模型的比较。

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

数据传输对象(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); } }
}

查看模型

视图模型与呈现模型相似,因为它是用于呈现视图的后备类。 然而,它与Presentation Model或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!
}

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

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的特定验证规则。
  • 视图模型定义了视图的外观(对于LabelForEditorForDisplayFor助手)。
  • 视图模型可以组合来自不同数据库实体的值。
  • 您可以使用DisplayFor或EditorFor helpers轻松指定视图模型的显示模板并在很多地方重复使用它们。

视图模型及其检索的另一个示例:我们要显示基本的用户数据,他的权限和用户名。 我们创建一个特殊的视图模型,其中只包含必需的字段。 我们从数据库中检索不同实体的数据,但是视图只知道视图模型类:

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
} 

很多大的例子,让我以清晰而脆弱的方式解释。

ViewModel =为创建视图而创建的模型。

ASP.NET MVC视图不能有多个模型,因此如果我们需要将多个模型的属性显示到视图中,这是不可能的。 ViewModel服务于此目的。

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

以下几个视图模型的例子

  • 要在视图页面中列出来自多个实体的数据 - 我们可以创建一个View模型并拥有我们想要列出数据的所有实体的属性。 加入这些数据库实体并设置视图模型属性并返回到视图以一种表格形式显示不同实体的数据
  • 视图模型可以仅定义视图所需的单个实体的特定字段。

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

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

以下是使用ViewModel列表数据的一个小例子。

希望这会有用。


我没有阅读所有的帖子,但每个答案似乎都缺少一个真正帮助我“获得它”的概念......

如果Model类似于数据库Table ,则ViewModel类似于数据库View - 视图通常要么从一个表中返回少量数据,要么从多个表(联接)中返回复杂的数据集。

我发现自己使用ViewModels将信息传递到视图/表单,然后在表单发回控制器时将该数据传输到有效的模型中 - 对于存储列表(IEnumerable)也非常方便。





viewmodel