ASP.NET MVC4 IN ACTION学习笔记-第四波

ASP.NET MVC4 IN ACTION –View Models

--这是本书的第二部分,前三波是第一部分,这波的知识真的很重要

--在这一波博客里,我会加入一些vs2010的编程技巧,这是在别人那里学不到的

--我会加入一些自己总结的EF方面技巧,这是这本书中暂时还没看到的

--内容我已经不打算按书中原汁原味的讲,因为这波开始,你将要真正的掌握MVC

--每个细节我都会抓的很细,讲书中省略的很多东西,但后期我可能不会这样去做了,因为要提高效率

--所以学习本系列MVC博客的人,可能不能跳着学习了,因为我讲过的就不会去再讲了

image原著:ASP.NET MVC 4 IN ACTION

本人能力有限,尽量将书中的知识浓缩去讲,仔细学过后,然后你再学习其他语言的MVC框架也就大同小异了

本次覆盖知识点:

  • 1. 用代码呈现用户界面(UI/User Interface)(Representing UI concepts in code)
  • 2. 定义一个页面使用的model(presentation model 或者上波我们提到的view model) (Defining the presentation model)
  • 3. 显示用户输入的数据 (Representing user input)
  • 4. 在复杂的场景中使用 (Scaling to complex scenarios)

 

     茗洋芳竹飞机票:第一波  第二波  第三波

 

 

    备注:controller action指controller中的action(也就是你们经常写的方法而已)

            controller actions指controller中全部actions

            action名字加action表示 某某名字的action,例如有个叫Index的action,我就会这样表示

            Index action.知道了吗?这方便我写博客,也更方便理解

            view model指页面(view)中用到model,我上篇博客说的视图模型就是view model

            action method指 返回值是ActionResult那个方法,比如Index那个action方法

            user interface,用户接口(UI的全称),你可以意会理解成页面,也就是要跟用户交互的东西,比如我们给用提供一个页面,让用户录入数据,这就是一个interface,一个接口,你暂且在这里就是view,就是页面

            business logic,商业逻辑,类似于数据访问层中业务实现的逻辑

            domain,你可以理解数据访问层

            domain model,数据访问层要使用到的model

            以后我想直接写英文了,好方便理解,在第二波的博客里我是吃过苦了,文章读起来好拗口

 

    关于本书的第一部分(前三波内容)讲了很多点到即止的知识,大致地概括了一下框架的某些部分。

     现在我们要更深入的研究这些细枝末节

     在本章我们将要讨论这个model,怎样为ASP.NET MVC框架特意地去设计一些更好结构的model。我们还要研究一下Model-View-Controller模式,model通常是很难理解的,因为model在很多地方用到的,说实话,view model,domain model,页面上的Model对象,@model等等。

     model中定义了很多字段,例如Student这个model定义了Name,id,classname,我们可以想象一下Student是个圆,这个大圆里面有很多小圆

image

假如程序运行了,会给程序一块内存用来存数据,Student这个圆可以理解为一整块,一个整体,然后细分Name一块,Class一块,Id一块,我感觉这就是数据结构了,不知道对不对。如果圆更多,是不是可以抽象理解成一个煤球形状的数据结构,如果数据是这样分布的,这里随便说着玩的,我是这样理解的。

干嘛说这些,因为这些数据都有意义,比如Name代表学生姓名,那么在view中我就可以直接model.Name就可以输出学生名字了。上一波我们也讲到了关于 view model要设计的更有意义和技巧,比如显示用户名 和 这个用户所发的所有comment数,view model中就2个字段,但是如果我们用domain model,那么显示不太方便。所以上面说要特意地去设计view model。

我感觉这句话挺好的:

When you work with object-oriented languages (such as C#), you create  classes that define this representation. You can create your representation so that when you use it you’re working in a more natural language that allows you to talk about the concepts represented by the software instead of using programming language constructs like Booleans, strings, and integers.

当你在使用面向对象的语言,比如C#,你定义了一个类,这个类定义了页面上该怎么显示,你可以创建你的表现形式,不需要再使用domain model(它里面可能含有很多boolean类型,string类型,int类型结构的数据),用更少的代码就可以表达出你想要显示的数据的形式,table显示或者div显示等等

一句话,domain model能满足view显示,你就显示,你觉得不方便,你可以新建一个model,我们称为 view model(或者presentation model),这个view model用来在页面更容易显示数据

通过本章,有些显示数据的问题你可以使用view model去简化在页面上呈现的逻辑,我们将要看一下view models和input models(把数据从view传递给controller,相当于添加数据等等)

 

5.1 什么是view model

          view model的目的很直接--它就是一个model,在一个view里面,为了方便操作数据而特意设计的一个model ,感觉如果还不懂,需要到项目中去意会了。

         在本节中,我们做一个简单的online store(在线商店)例子来讲解view model怎么工作的。了解一下,把view model传递给view时候的时候,domain model和view model传递的区别,最后我们看一下,input models是如何把view上的数据传递给controller的。

 

 

5.1.1 Online Store例子

            准备工作(书中没有,我大致写下让你复习一下)

           image新建项目

            image默认,也添加测试模版,单击image

           image添加EntityFramework.SqlServerCompact

            image正在安装…

 

image在Models文件夹中新建一个StoreOnlineContext类

修改这个类,让它继承DbContext,按Shift+Alt+F10快速导入命名空间

image

添加一个构造函数,参数,是我们的数据库名字

image

接下来,我顺便教大家快捷方式吧,写代码就要快才好,下次我就不写了

直接在这个类继续编写,写下如下代码

 image

将光标置入 Customer中去,按下Ctrl+Shift+F10,然后回车键,可以快速生成对应名称的类

同理我们继续,完成后的样子

image

然后我们去掉 那个ServiceLevel的DbSet

image

接下来我们向实体类中添加代码

Customer.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  namespace OnlineStore.Models
   8:  {
   9:      public class Customer
  10:      {
  11:          [Key]
  12:          public int ID { get; set; }
  13:          public int Number { get; set; }
  14:          public string FirstName { get; set; }
  15:          public string LastName { get; set; }
  16:          public bool Active { get; set; }
  17:          public ServiceLevel ServiceLevel { get; set; }
  18:          public ICollection<Order> Orders { get; set; }
  19:      }
  20:  }

 

ServiceLevel.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public enum ServiceLevel
   9:      {
  10:          Standard,
  11:          Premier 
  12:      }
  13:  }

 

Order.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  namespace OnlineStore.Models
   8:  {
   9:      public class Order
  10:      {
  11:          [Key]
  12:          public int ID { get; set; }
  13:          public DateTime Date { get; set; }
  14:          public ICollection<Product> Product { get; set; }
  15:          public decimal TotalAmount { get; set; }
  16:      }
  17:  }

 

Product.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  namespace OnlineStore.Models
   8:  {
   9:      public class Product
  10:      {
  11:          [Key]
  12:          public int ID { get; set; }
  13:          public string Name { get; set; }
  14:          public decimal Cost { get; set; }
  15:      }
  16:  }

 

接下来我们打开HomeController  添加一个新的action,添加好后,我们便来添加好对应的试图

   1:    StoreOnlineContext _db = new StoreOnlineContext();
   2:   
   3:          public ActionResult Create()
   4:          {
   5:              //添加测试数据
   6:              //1.添加产品
   7:              Product pro1 = new Product
   8:              {
   9:                  ID=1,
  10:                  Name = "大话设计模式",
  11:                  Cost = 39.98M
  12:              };
  13:              Product pro2 = new Product
  14:              {
  15:                  ID = 2,
  16:                  Name = "  WCF服务编程:.NET开发者决战SOA的制胜利剑(第3版) [平装]",
  17:                  Cost = 88.5M
  18:              };
  19:              Product pro3 = new Product
  20:              {
  21:                  ID = 3,
  22:                  Name = "  WCF全面解析(套装上下册)",
  23:                  Cost = 126M
  24:              };
  25:              ICollection<Product> pros = new List<Product> { pro1,pro2,pro3};
  26:              //2.添加订单
  27:              Order order = new Order();
  28:              order.ID = 1;
  29:              order.Date = DateTime.Now;
  30:              order.Product = pros;
  31:              order.TotalAmount = 254.48M;
  32:              //3.添加客户
  33:              Customer customer = new Customer();
  34:              customer.FirstName = "杨";
  35:              customer.LastName = "洋";
  36:              customer.Number = 10000;
  37:              customer.ServiceLevel = ServiceLevel.Standard;
  38:              customer.Active = true;
  39:              ICollection<Order> orders = new List<Order> { order };
  40:              customer.Orders = orders;
  41:              _db.Customers.Add(customer);
  42:              _db.SaveChanges();
  43:              return Content("添加成功!");
  44:          }

对应的Create页面

image

接下来按下F5运行,默认只有 localhost:端口号,显示的Home中的Index action的内容

修改浏览器地址栏:localhost:你的端口号/Home/Create,过一会,会显示添加成功

关闭窗口,关掉调试,我们就会发现生成了一个sdf文件,这个是我们第一波博客里面说的SqlServer Compact

image如果没发现,请点击显示全部文件,或者刷新一下解决方案资源管理器

右键该数据库,将它包含到项目中去,我们右键打开该文件,就会发现生成的表文件了

imageimage

image

image

image

到这里,我们的数据基本初始化完成了,数据虽然少,但是我们主要是体现的编程思想,下面开始吧!

我们现在有一个这样的需求:

这个系统的管理员,想要总结Customer的 名字,在线状态,等级状态,Order数量,最近的订单时间

一种方案是:我们用domain model完成,将数据处理后放到页面展示。我们从数据库中取出Customers,然后把它传给view,在view里面,我们循环这个customer集合,然后构造个table展示,在最后一列(最近订单日期),在view中又要遍历Customers中的Orders集合,处理一下,然后找出最近的订单日期。

这种方法会感觉在view上很复杂,秉着尽可能地设计可维护性好的程序的理念,这种方法就要放弃----复杂的循环和计算应该在更高级的一层去处理,view要做的事情就是展示处理后的结果。(这里我又添加了一条数据,但是这次我把Id手动赋值给去掉了,因为数据库中我给Id那个属性上面加了Key特性,说明它是个主键,自动增长列,ICollection会自动理解为外键)

image

我们创建一个view model来解决这个问题。

5.1.2 创建一个View Model

         在Customer Summary页面上使用一个view model是很正确的。我们这样设计我们在Models文件中添加一个CustomerSummary.cs文件)

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public class CustomerSummary
   9:      {
  10:          public string Name { get; set; }
  11:          public string Active { get; set; }
  12:          public string ServiceLevel { get; set; }
  13:          public string OrderCount { get; set; }
  14:          public string MostRecentOrderDate { get; set; }
  15:      }
  16:  }

这里面的结构很简单,都是string类型的。对的,我们所要做的就是在页面上显示文本(text)。这样看起来,简单直接,很容易见看懂了,不是吗?presentation  model设计理念,就是在一个view里面,presentation model的设计,要将view要呈现的model的难度降到最低。

这里我解释一下:presentation  model、view model、domain model的区别:

presentation  model是页面上最终要使用的一类model的总称,可以是view model,也可以是domain model(当然,domain model显示的数据比较简单,已经不需要在专门去设计一个view model了,它也可以充当presentation  model)。view model是为了方便view显示数据而有意,有技巧的而设计的model。

打个比方:presentation 对应  一场展览晚会,model对应服装,平时domain风格的model也可以去参加这场晚会,有时场合合适,但是有时不适合,在晚会里会遇到很多坎坷,所以我们有必要去换一个风格的服装,比如view风格的model,它也可以去参加这场晚会,而且精心设计的,我相信view风格的在presentation 表现上更出色。所以我会去选择view model

不知道你们有没有听懂?(哎,这三个英文单词的区别,我是搞懂了,不知道,你有没有搞懂?)

5.1.3 把数据传到 Presentation Model

     在我们的应用程序中,我们等下会建立presentation model,这个model结构和domain model的结构有时几乎一模一样的,你可能会怀疑,有必要再建立一个model吗?用domain model不就够了吗,写两个model不麻烦吗?通常如果你用Entity framework框架生成的实体已经够做domain model了,但有时你真的不能怕麻烦,而不去再建立presentation model,宁愿你把ef中生成的实体,你拷贝过去都行,都随便了,只要有都行,我们真正要在页面上使用的model都是presentation model,这样比如 domain model 满足不了页面要显示的额外数据,我们就可以在presentation model中拓展要显示的属性就行了,view中使用@model关键字 声明一个强类型的视图的时候,只能是一个model,假如一个页面要显示的数据来自两个model怎么办?view中@model只能使用一次,所以你就只能把要显示的所有内容封装到一个model中去,而这些数据都来自domain model中的,有的在domain层,可能都是要通过逻辑计算后才能得到的结果,比如 用户名 发帖数 最近访问时间,这个发帖数使用通过计算得到的,除非你数据库设计时,就一张表,字段就这个3个,那无所谓了,随便了,而view中就只是要显示这3个字段,所以我们可以建立一个presentation model,里面就这3个字段,全string型的,方便显示了不是吗?而view中@model 关键字引入的model就是presentation model类型的,就行了.所有复杂的问题都解决了.

每次都要写presentation model的确很烦,不过不用担心,这个问题,已经有人考虑了,我们可以使用AutoMapper工具帮我们对presentation model和domain model的相互映射,因为假如 domain model中有很多属性要赋值到presentation model中去,一行一行写代码确实很烦,所以使用AutoMapper就能很快帮我们解决,一行代码就行了.(我会在第11章讲)

(这段内容都是我自己加的,这一波开始,我发现书中把看书者的地位已经放在你已经做过ASP.NET MVC项目了,内容简写了好多)

后期你写项目的时候,就应该能感受到方便了.我们把要显示的数据使用Presentation model思想封装的那么好,页面上很容易就显示数据了,所以我们就能把开发的重点放在HTML和css样式,布局前端上面去了,难道不是吗?

这样做,反而使的程序更容易测试,维护,开发.写到这里,我想到一个想法,团队开发中,我们可以先写好Presentation model,domain model直接利用工具生成,写好后,后台程序员直接经过逻辑,将结果封装成presentation model去就行了.前端只要Model点属性不就够了,真的很简单.

在controller中创建presentation model是不好的,因为controller负责的职责已经很多了,它要决定哪个view被呈现,还有很多其他的工作,所以不要给controller添麻烦了,把这个model提取出去,新建一个类也可以

我们新建一个CustomerSummaryController

image

代码:

   1:  using OnlineStore.Models;
   2:  using System;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using System.Web;
   6:  using System.Web.Mvc;
   7:   
   8:  namespace OnlineStore.Controllers
   9:  {
  10:      public class CustomerSummaryController : Controller
  11:      {
  12:          //
  13:          // GET: /CustomerSummary/
  14:          StoreOnlineContext _db = new StoreOnlineContext();
  15:          public ActionResult Index()
  16:          {
  17:              IEnumerable<CustomerSummary> model = from o in _db.Customers.ToList<Customer>()
  18:                      select new CustomerSummary
  19:                      {
  20:                          Name = o.FirstName + o.LastName,
  21:                          Active = o.Active? "是" : "否",
  22:                          ServiceLevel =  Enum.GetName(typeof(ServiceLevel), o.ServiceLevel).ToString(),
  23:                          OrderCount = o.Orders == null ? "0" : o.Orders.Count().ToString(),
  24:                          MostRecentOrderDate=(from sub in o.Orders
  25:                                                                orderby sub.Date descending
  26:                                                                select sub).Count()>0?(from sub in o.Orders
  27:                                                                orderby sub.Date descending
  28:                                                                select sub).FirstOrDefault().Date.ToString("yyyy年MM月dd日"):"还没有订单"
  29:                      };
  30:              return View(model);
  31:          }

在这里,你发现了,我_db.Customers.ToList<Customer>()

本来是linq  to entity操作,被我转成了linq to object操作,因为我在linq to entity遇到一个问题,就是ToString的问题,linq to entity不支持ToString,不信你可以试试

在这个方法中,我们返回的是一个 View(对象),这个对象还是一个集合

 

5.1.4 ViewData.Model

Controller和View都共享一个名叫ViewData.ViewData(键值对形式的一个很正常的集合),类型叫ViewDataDictionary的一个object

在上个代码中,我们最后使用了return View(model),这个ViewData.Model就会自动的被赋值了,这里被赋值了一个IEnumerable<CustomerSummary>类型的集合,这个对象在view被呈现之前,就已经全部准备好了,所以你在view中就可以使用这个对象,并使用它里面的值了.这个Model属性是强类型的,所以我们的view知道怎样去预测,开发者就可以在vs中就会有智能提示,得到这个好处.这些内部的工作,大部分的Razor视图引擎都会帮我们做好了,我们使用@model关键字

@model IEnumerable<OnlineStore.Models.CustomerSummary>

image

接着上面的那个action,我们添加对应的视图,代码如下:

   1:  @{
   2:      ViewBag.Title = "Index";
   3:  }
   4:  @model IEnumerable<OnlineStore.Models.CustomerSummary>
   5:  <style>
   6:       td,th{ height:30px; text-align:center;width:20%}
   7:       table td,th{ border:  #0094ff  solid  1px ;}
   8:      table {width:100%;}
   9:  </style>
  10:  <h2>Custommer Summary</h2>
  11:  <table>
  12:      <tr>
  13:          <th>Name</th>
  14:          <th>Active?</th>
  15:          <th>Service Level</th>
  16:          <th>Order Count</th>
  17:          <th>Most Recent Order Date</th>
  18:      </tr>
  19:      @foreach (var summary in Model)
  20:              {
  21:          <tr>
  22:              <td>@summary.Name</td>
  23:              <td>@summary.Active</td>
  24:   
  25:              <td>@summary.ServiceLevel</td>
  26:   
  27:              <td>@summary.OrderCount</td>
  28:              <td>@summary.MostRecentOrderDate</td>
  29:          </tr> 
  30:              }
  31:  </table>

估计你也看懂了,怎么去使用了.

5.2 表现层的用户输入(user input)

就像我们精心设计一个presentation model来呈现数据一样,我们也要精心设计一个model来获得用户输入的数据.在view里,我们使用很厉害的presentation model使工作变得很简单,当然一个很厉害的input model也可以在解决获得用户输入的数据后添加到数据库里这个问题上变得很简单.以前在ASP.NET里面我们都是通过request请求上key获得对应的view来获得数据然后来处理的。这里我们使用input model

5.2.1 设计model

我们新建一个类

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public class NewCustomerInput 
   9:            { 
  10:                 public string FirstName { get; set; }                  
  11:                 public string LastName { get; set; }                                                
  12:                 public bool Active { get; set; } 
  13:            }
  14:  }

这个model主要接受用户输入的数据,跟view都是一一对应的

image

5.2.2 设计model

接下来我们在CustomerSummaryController中添加一个action

   1:        public ViewResult Save(NewCustomerInput input)
   2:          {
   3:              return View(input);
   4:          } 

添加完NewCustomerInput,F5运行下程序,然后我们再右键Save名称,添加对应的view

image选中后,我们点确定image

当然我们可以不选择模型类,自己添加对应的view后,自己手动加代码,这里只是列出一个技巧

在这个Save action中,我们传了一个NewCustomerInput类型对象的参数,这个值就会被ASP.NET MVC中的DefaultModelBinder相互绑定,view中就可以把值封装好后传过来,在ASP.NET MVC默认是这样定义的.

我们添加好view后,我们就成功把一个view变成一个强类型的view,ViewPage<T>,其中T已经被指定为NewCustomerInput对象了,也就意味着,ViewData.Model对象已经是NewCustomerInput类型的了。然后我们在精心设计一个form(表单)就可以完成了.

   1:  @model OnlineStore.Models.NewCustomerInput
   2:   
   3:  @{
   4:      ViewBag.Title = "Save";
   5:  }
   6:  <div>
   7:      <form action="@Url.Action("Save")" method="post">
   8:          <fieldset>
   9:              <div>
  10:                  @Html.LabelFor(x => x.FirstName)
  11:                  @Html.TextBoxFor(x => x.FirstName)
  12:              </div>
  13:              <div>
  14:                  @Html.LabelFor(x => x.LastName)
  15:                  @Html.TextBoxFor(x => x.LastName)
  16:              </div>
  17:              <div>
  18:                  @Html.LabelFor(x => x.Active)
  19:                  @Html.CheckBoxFor(x => x.Active)
  20:              </div>
  21:              <div>
  22:                  <button name="save">
  23:                      保存</button>
  24:              </div>
  25:          </fieldset>
  26:      </form>
  27:  </div>

image指定了这个表单post到哪个action去处理,这里指定了Save

还有我们使用了lambda表达式,绑定NewCustomerInput中的属性

例如,@Html.TextBoxFor(x=> x.LastName)等同于代码

<input type="text" name="LastName" />

你写这行代码和C#风格的绑定效果都是一样的.

  Lambda在重构(refactoring)中的帮助

   不要小看view中的lambda表达式,它们在你写代码的时候就在编译,所以你改了action,这个代码就会在编译的时候报错,所以在你的view中写代码时候,对比下你视图中引用的类,还有返回string的方法--你只有在运行的时候会发现这些错误.

   在refactoring中当然还有其他的强类型视图数据引用助手,我们可以使用JetBrains ReSharper(www.jetbrains.com/resharper),它将帮助你重构代码,所有的视图都可以使用它,一个很强大的工具

 

再使用强类型助手之前,我们还是依靠 magic(魔力的)strings,编程者们要手动地去确保它们和input form一致,使用强类型助手,就像上面view中的代码,ASP.NET MVC已经为我们处理了,所以把input model中的属性改名了,也不会在屏幕上显示错误,程序顺利运行,但是打开错误的页面,还是会显示绑定错误的.

image这里FirstName后我故意添加了一个s

按下F5的时候,程序可以运行,无影响,但是指定到使用页面的时候,就会显示这个绑定的错误.就是这个效果

5.2.3 处理提交的input model

在上面那个view中,我们post到Save action,ASP.NET MVC提供了一个很简单的方式,把HTTP请求转换成一个model,这个过程我们叫model binding,第十章我们会仔细去讲,我们看一下这个action

image

接下来我们新添一个ActionResult Save2,上面那个action返回的是ViewResult

image

修改对应的view中post请求的action,为Save2

image

按下F5运行项目,输入新地址

image

image

image

然后输入http://localhost:<你的port>/CustomerSummary/Index

image

我们再修改下Save2 action,return 新的action---Index,这样添加成功后,就会跳转到Index action,而Index action返回的是一个view,也就是Customer Summary列表

image

小提示:修改view中的代码的时候,修改好后,保存,不必重新运行项目,直接刷新浏览器就可以立即看到刷新后的效果

如果程序的服务器没有关,但是vs中的代码已经停止运行了,此时修改view中的代码,然后保存,也不必重新运行项目,直接刷新浏览器就可以立即看到刷新后的效果.就等同于修改一个html一样,很方便,但是修改后台的代码,就不行了,就必须要重新运行项目.

许多view不会去显示或者一个input forms,而是整合很多elements(网页上一级一级的节点),来达到一个富客户端的体验.在一下节里,我么将会应用这些我们在本波里已经学到的concept(概念或者思想),来实现一个更复杂的view

 

5.3 更复杂的model,display和input并存

在这一节里面,我们将构造一个view model,既显示数据,也把input model中最后修改的active状态数据发送到服务器上,做修改工作

image

5.3.1 设计展示和Input都存在的model

我们新建一个CustomerSummary2类

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public class CustomerSummary2
   9:      {
  10:              public string Name { get; set; }
  11:              public string ServiceLevel { get; set; }
  12:              public string OrderCount { get; set; }
  13:              public string MostRecentOrderDate { get; set; }
  14:            public CustomerSummaryInput inputs { get; set; }
  15:          public class CustomerSummaryInput {
  16:              public int Number { get; set; }
  17:              public bool Active { get; set; }
  18:          }
  19:      }
  20:  }

 

这里我们在一个类中嵌套另一个类,之后,在user interface上,在显示的时候,input elements就会被嵌套,把CustomerSummaryInput这个Input model写在这里面,方便以后的维护,因为他和这一个view在一起显示,在其他view中也没有用到啊

注意在CustomerSummaryInput中的Number属性--它是每一个customer中的编号(Id),现在我们冻结叫杨洋的这个customer

5.3.2 让Input Model开始工作

我们再新建一个action,MoreSave action,Save3 action

   1:    public ViewResult MoreSave()
   2:          {
   3:              IEnumerable<CustomerSummary2> model = from o in _db.Customers.ToList<Customer>()
   4:                                                   select new CustomerSummary2
   5:                                                   {
   6:                                                       Name = o.FirstName + o.LastName,
   7:                                                       ServiceLevel = Enum.GetName(typeof(ServiceLevel), o.ServiceLevel).ToString(),
   8:                                                       OrderCount = o.Orders == null ? "0" : o.Orders.Count().ToString(),
   9:                                                       MostRecentOrderDate = (from sub in o.Orders
  10:                                                                              orderby sub.Date descending
  11:                                                                              select sub).Count() > 0 ? (from sub in o.Orders
  12:                                                                                                         orderby sub.Date descending
  13:                                                                                                         select sub).FirstOrDefault().Date.ToString("yyyy年MM月dd日") : "还没有订单",
  14:                                                       inputs=new CustomerSummary2.CustomerSummaryInput{
  15:                                                        Active=o.Active,
  16:                                                        Number=o.Number
  17:                                                       }
  18:                                                   };
  19:              return View(model);
  20:          }
  21:   
  22:          [HttpPost]
  23:          public ActionResult Save3(List<CustomerSummary2.CustomerSummaryInput> input)
  24:          {
  25:              foreach (var item in input)
  26:              {
  27:                  Customer cus = _db.Customers.Where(x => x.Number == item.Number).FirstOrDefault();
  28:                  cus.Active = item.Active;
  29:                  _db.SaveChanges();
  30:              }
  31:              return RedirectToAction("MoreSave");
  32:          }

 

我们添加对应的视图:

   1:  @model IEnumerable<OnlineStore.Models.CustomerSummary2>
   2:  @{
   3:      ViewBag.Title = "MoreSave";
   4:  }
   5:  <style>
   6:      td, th {
   7:          height: 30px;
   8:          text-align: center;
   9:          width: 20%;
  10:      }
  11:   
  12:      table td, th {
  13:          border: #0094ff solid 1px;
  14:      }
  15:   
  16:      table {
  17:          width: 100%;
  18:      }
  19:  </style>
  20:  <h2>Custommer Summary</h2>
  21:  <form action="@Url.Action("Save3")" method="post">
  22:      <table>
  23:          <tr>
  24:              <th>Name</th>
  25:              <th>Service Level</th>
  26:              <th>Order Count</th>
  27:              <th>Most Recent Order Date</th>
  28:              <th>Active?</th>
  29:          </tr>
  30:          @foreach (var summary in Model)
  31:          {
  32:              <tr>
  33:                  <td>@summary.Name</td>
  34:                  <td>@summary.ServiceLevel</td>
  35:   
  36:                  <td>@summary.OrderCount</td>
  37:                  <td>@summary.MostRecentOrderDate</td>
  38:                  <td>
  39:                      <input type="checkbox" name="Active" checked="@summary.inputs.Active"/>
  40:                      <input type="hidden" name="Number" value="@summary.inputs.Number" />
  41:                  </td>
  42:              </tr> 
  43:          }
  44:      </table>
  45:      <div>
  46:          <button name="save">
  47:              修改Active</button>
  48:      </div>
  49:  </form>

按下F5运行,就可以看到一个列表的效果,Active列已经自动帮我们勾选上了

接下来修改active,关于这个,这里的Save3没有成功实现,还有问题,如果让我实现这个功能,我不会采用嵌入类的形式的,我自己试着用嵌入类的方式去写了,但是没有成功,你们能帮我解决吗?

在这一波教程里面,书里面省略了很多的代码,而且都简化了,博客写到这里,还有点可惜,因为把修改后的状态保存回去,还没有完成.

这一波的代码我就不上传了,手写练习吧,如果你有利用嵌入类完成了最后的这个效果,你能告诉我思路吗?

 

在下一波的教程里,我们主要讲Validation ,验证,再下下一波里,我们主要讲AJAX方面的,这两波内容都很多,也都是很重要的部分

关于ASP.NET MVC4 IN ACTION系列目录地址已经生成:点击查看目录

 

 

posted @ 2013-04-21 07:49  AYUI框架  阅读(2818)  评论(18编辑  收藏  举报