知识点5-1:视图模型

对于一个在线商店,域模型可能由表现产品、订单、客户等的类所组成,它对定义这些实体的数据和业务规则都进行了封闭,这种模型用作建立用户界面以及定义业务规则的基础。尽管这种办法可能适合某些应用程序(通常是有简单域的小型应用程序),但经常会带来麻烦,特别是当应用程序增长,且要求UI偏离业务逻辑需求时,一个利害冲突可能会导致过于复杂和不可维护的软件。

这个问题的解决方法是引入视图模型(View Model),以简化渲染用户界面所需的逻辑。我们将考擦如何定义视图模型,以及用来将用户界面的数据回发给控制器层的输入模型。

一、什么是视图模型

视图模型的目的十分简单,它是一个专门为用于视图而设计的模型,它提供了一个建立在域模型之上的简化接口,以保持视图决策最小化。

1.留言本例子中添加视图模型

在留言本这个例子中,GuestbookEntry类既作为域模型,也作为视图模型。它既表现了数据库中存储的数据,也表现了用户界面中的字段。

对于像留言簿这样的小型应用程序,这是足够的。但是,随着应用程序复杂性的提升,当复杂的用户界面结构必须不直接映射模型的结构时,即视图数据与模型结构不同,往往需要将两者分开。比如,让我们对Guestbook应用程序添加一个新的页面,以显示每个用户已递交了多少评论的摘要,如图所示。

为了创建这一屏幕,首先需要创建一个视图模型,它每一列含有一个属性——用户名和已递交的评论数:

    public class CommentSummary
    {
        public string UserName { get; set; }
        public string NumberOfComments { get; set; }
    }

现在需要创建一个控制器动作,查询数据库以获取显示所必需的数据,然后将其注入CommentSummary类实例。

      public ActionResult CommentSummary()
        {
            var entries = from entry in _db.Entries
                          group entry by entry.Name into groupedByName
                          orderby groupedByName.Count() descending
                          select new CommentSummary
                          {
                              NumberOfComments = groupedByName.Count(),
                              UserName = groupedByName.Key
                          };
            return View(entries.ToList());
        }

这里使用了LINQ来查询留言簿数据,并按用户名对递交的评论进行分组。接着将数据投影成视图模型实例,然后便可以将其传递给视图。

@model IEnumerable<Guestbook.Models.CommentSummary>

<table>
    <tr>
        <th>Number of comments</th>
        <th>User name</th>
    </tr>
    @foreach(var summaryRow in Model)
    {
        <tr>
            <td>@summaryRow.NumberOfComments</td>
            <td>@summaryRow.UserName</td>
        </tr>
    }
</table>

2.在线商店示例

让我们从一个简单的在线商店示例开始。它可能包含Customer、Order和Product类,这些类对应于关系数据库中的表,并使用对象关系映射器进行映射。

    public class Customer
    {
        public int Number { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool Active { get; set; }
        public ServiceLevel ServiceLevel { get; set; }
        public IEnumerable<Order> Orders { get; set; }
    }

    public enum ServiceLevel
    {
        Standard, Premier
    }

    public class Order
    {
        public DateTime Date { get; set; }
        public IEnumerable<Product> Product { get; set; }
        public decimal TotalAmount { get; set; }
    }

    public class Product
    {
        public string Name { get; set; }
        public decimal Cost { get; set; }
    }

商店的管理区可能包含一个Customer Summary(客户摘要)页面,该页面列出每个客户及其订单数。

建立这个UI的一个可选办法是可以直接通过域模型来建立该屏幕。我们可以从数据库取回客户列表,然后将其传递给视图,视图循环遍历客户列表并构建表格。当到达最后一列Most Recent Order Date(最近的订单日期)时,视图不得不循环遍历客户的Orders集合,以得出最近的一份订单。

这种方法的一个问题,是它使视图十分复杂。为了使视图尽可能是可维护的,它应该尽可能简化,将复杂的循环和计算逻辑放在更高层执行,视图唯一应该做的只是显示这种计算的结果。通过实现明确表示该表格的视图模型,可以做到这一点。

(1)建立视图模型

    public class CustomerSummary
    {
        public string Name { get; set; }
        public string Active { get; set; }
        public string ServiceLevel { get; set; }
        public string OrderCount { get; set; }
        public string MostRecentOrderDate { get; set; }
    }

(2)交付表现模型

 public class CustomerSummaryController : Controller
    {
        private CustomerSummaries _customerSummaries = new CustomerSummaries();

        public ActionResult Index()
        {
            IEnumerable<CustomerSummary> summaries = _customerSummaries.GetAll();

            return View(summaries);
        }
    }
public class CustomerSummaries
    {
        public IEnumerable<CustomerSummary> GetAll()
        {
            return new[]
            {
                new CustomerSummary
                {
                    Active = "Yes",
                    Name = "John Smith",
                    MostRecentOrderDate = "02/07/10",
                    OrderCount = "42",
                    ServiceLevel = "Standard"
                },
                new CustomerSummary
                {
                    Active = "Yes",
                    Name = "Susan Power",
                    MostRecentOrderDate = "02/02/10",
                    OrderCount = "1",
                    ServiceLevel = "Standard"
                },
                new CustomerSummary
                {
                    Active = "Yes",
                    Name = "Jim Doe",
                    MostRecentOrderDate = "02/09/10",
                    OrderCount = "7",
                    ServiceLevel = "Premier"
                },
            };
        }
    }

(3)ViewData.Model
      控制器与视图共享了一个ViewDataictionary类型的对象,其名称为ViewData。它有一个颇具特色的Model属性。当我们在清单5.3中调用return View(summaries)时,ViewData.Model会自动以CustomerSummary对象列表进行填充,这便做好了在视图中显示的准备。Model属性也是强类型的,因此视图确切地知道所期望的类型,也使开发者能够利用IDE智能感应之类的特性,以及对变量重命名的支持。Razor视图引擎遮盖了其中大部分内部机制,这使得定义模型类型变得简单。视图可以用@model指示符来描述它的模型类型:

@model IEnumerable<StoreCh05.Models.CustomerSummary>
    <table>
        <tr>
            <th>Name</th>
            <th>Active?</th>
            <th>Service Level</th>
            <th>Order Count</th>
            <th>Most Recent Order Date</th>
        </tr>
        @foreach (var summary in Model)
        {
            <tr>
                <td>@summary.Name</td>
                <td>@summary.Active</td>
                <td>@summary.ServiceLevel</td>
                <td>@summary.OrderCount</td>
                <td>@summary.MostRecentOrderDate</td>
            </tr>
        }
    </table>

 二、表现用户输入

一个强大的表现模型能够使视图易于使用数据,一个强大的输入模型也能够使应用程序易于使用用户输入。代替那种使用易于出错的字符串键和检测希望与输入元素名匹配的请求值,我们可以利用ASP.NET MVC的特性来使用强大的输入模型。

1.设计输入模型

图中的简单表单有两个文本框和一个复选框。作为一种应用程序的特征,该表单也值得作为一个正式化的表现:一个类。

public class NewCustomerInput
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool Active { get; set; }
  }

2.在视图中表示输入模型

public class CustomerController : Controller
    {
        public ViewResult New()
        {
            return View();
        }

        public ViewResult Save(NewCustomerInput input)
        {
            return View(input);
        }
    }
@model StoreCh052.Models.NewCustomerInput
<div>
    <form action="@Url.Action("Save")" method="post">
        <fieldset>
            <div>
                @Html.LabelFor(x => x.FirstName)
                @Html.TextBoxFor(x => x.FirstName)
            </div>
            <div>
                @Html.LabelFor(x => x.LastName)
                @Html.TextBoxFor(x => x.LastName)
            </div>
            <div>
                @Html.LabelFor(x => x.Active)
                @Html.CheckBoxFor(x => x.Active)
            </div>
            <div>
                <button name="save">
                    Save</button>
            </div>
        </fieldset>
    </form>
</div>

三、用于显示和输入的复杂模型

时间: 2024-10-08 18:33:44

知识点5-1:视图模型的相关文章

知识点3-5:在视图中显示视图模型数据

通常为了在视图中显示信息,可能会使用HtmlHelper(HTML辅助器)对象,以协助获取视图模型来生成HTML.考虑以下清单,它渲染完整的留言本条目. 在这个留言本条目详情屏幕中,我们显示了在模型中传递的留言本详细信息.接下来,用Razor的多行代码语句,以接受ViewData中的“hasPermission”值.Razor的多行代码语句是一个@{}代码块.最后,使用了一个Razor的if语句,有条件地显示编辑链接. 在登录页面中,我们使用了一个视图模型对象来表示整个表单,如清单3.6所示.

利刃 MVVMLight 2:Model、View、ViewModel结构以及全局视图模型注入器的说明

上一篇我们已经介绍了如何使用NuGet把MVVMLight应用到我们的WPF项目中.这篇我们来了解下一个基本的MVVMLight框架所必须的结构和运行模式. MVVMLight安装之后,我们可以看到简易的框架布局,如上篇,生成了一个ViewModel文件夹,ViewModel层的内容都放在这边,除了Main对象的ViewModel之外,还包含一个ViewModelLocator文件, 用来注入当前的ViewModel全局实例. 一.先来说说分层结构: 如图: 1.View负责前端展示,与View

视图模型

每个视图定义一个对象模型可以帮助你跟踪视图的真实所需.所以应该为每一个应用程序中的视图定义视图模型类. 每个视图都应该有一个视图模型类.可以使用控制器名称和视图名称的一个组合.例如,从Home控制器调用的命名Index的视图,其视图模型对象可能被命名为HomelndexViewModel. 更好的做法是,可以在Models文件夹中创建一个名为Home的子文件夹,并在其中建立一个IndexViewModel类. 那么如何开发一个视图模型类呢? 首先,视图模型对象是一个只有数据而(几乎)没有行为的普

视图模型和分页

一. 视图模型关联模型可以解决一对一.一对多和多对多等关联操作.它们通过生成多条 SQL 语句然后进行数组拼装得到最终想要的结果.对于一对一这种 HAS_ONE 和 BELONGS_TO 的多表关联查询,可以使用视图模型.它采用的是联合查询(JOIN),非常适合此类查询需求. 在 WeiBo/Home/Controller/UserController.class.php 中的代码为: 1 class UserController extends Controller { 2 public fu

ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第三章:搜索、高级过滤和视图模型

在这一章中,我们首先添加一个搜索产品的模块以增强站点的功能,然后使用视图模型而不是ViewBag向视图传递复杂数据. 注意:如果你想按照本章的代码编写示例,你必须完成第二章或者直接从www.apress.com下载第二章的源代码. 3.1 添加产品搜索 为了执行产品搜索,我们将添加一些功能使其能够按照产品名称.描述和分类进行搜索,从而让用户有一个更好的选择来查找相关结果. 之所以将分类也包含在内,是因为如果用户输入的是"clothes",而不是一件特定的衣服,那么所有的衣服都会被搜索到

架构蓝图--软件架构 &quot;4+1&quot; 视图模型

引言 我们已经看到在许多文章和书籍中,作者欲使用单张视图来捕捉所有的系统架构要点.通过仔细地观察这 些图例中的方框和箭头,不难发现作者努力地在单一视图中表达超过其表达限度的蓝图.方框是代表运行的程序吗?或者是代表源代码的程序块吗?或是物理计算机 吗?或仅仅是逻辑功能的分组吗?箭头是表示编译时的依赖关系吗?或者是控制流吗?或是数据流吗?通常它代表了许多事物.是否架构只需要单个的架构样式?有 时软件架构的缺陷源于过早地划分软件或过分的强调软件开发的单个方面:数据工程.运行效率.开发策略和团队组织等.

ThinkPHP中视图模型详解.

很多TP的新手对于模型中的视图模型不甚了解,官方虽然有详细手册,但是对于初学者来说还是比较难以理解! 先简单说一下视图模型所能实现的功能,基本就是主表与副表之间各个字段的关联问题,实现多表关联查询,相对于使用原生SQL语句来说,着实是简单不少. 首先在数据表初始化的时候,用的不是传统的M('User'),在视图模型中使用的是D('UserView'),如下图: 需要主要的是几个单词的首字母大写. 之后在Lib/Model目录中新建名为XXXViewModel.class.php(XXX为任意控制

CodeIgniter视图 &amp; 模型 &amp; 控制器

-------------------------------------------------------------------------------------------------------- 载入视图 $this->load->view('home/name'); //可以用子文件夹存储视图,默认视图文件以'.php'结尾 载入多个视图 $data['title'] = 'chenwei'; //给视图添加动态数据 $data['message'] = 'Your messa

thinkphp 视图模型使用分析

<?php /** * 视图模型 * */ class ViewBatchModel extends ViewModel{ public $viewFields = array( 'Jinxiao_batch'=>array('id','orderid','proid','subid','time','productdate','intobatch','num'), 'Jinxiao_product'=>array('id'=>'productID','default2'=>